diff options
782 files changed, 31304 insertions, 6998 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/android.txt b/Documentation/devicetree/bindings/arm/msm/android.txt index db52284892af..7b8b7909bae3 100644 --- a/Documentation/devicetree/bindings/arm/msm/android.txt +++ b/Documentation/devicetree/bindings/arm/msm/android.txt @@ -1,6 +1,6 @@ Android firmware -Node to specify early mount of vendor partition. +Node to specify early mount of vendor and system partition. Required properties @@ -52,3 +52,35 @@ Example: }; }; }; + +system: +----------------- + +system partition specification. + +Required properties: + +-compatible: "android,system" +-dev: block device corresponding to system partition +-type: file system type of system partition +-mnt_flags: mount flags +-fsmgr_flags: fsmgr flags + +Example: + + firmware: firmware { + android { + compatible = "android,firmware"; + fstab { + compatible = "android,fstab"; + system { + compatible = "android,system"; + dev = "/dev/block/platform/soc/1da4000.ufshc/by-name/system"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,slotselect"; + status = "ok"; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/arm/msm/diag_mhi.txt b/Documentation/devicetree/bindings/arm/msm/diag_mhi.txt new file mode 100644 index 000000000000..02f5103d5563 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/diag_mhi.txt @@ -0,0 +1,12 @@ +Qualcomm Technologies, Inc. Diag MHI Driver + +Required properties: +-compatible : should be "qcom,diag-mhi". +-qcom,mhi : phandle of MHI Device to connect to. + +Example: + qcom,diag { + compatible = "qcom,diag-mhi"; + qcom,mhi = <&mhi_wlan>; + }; + diff --git a/Documentation/devicetree/bindings/arm/msm/imem.txt b/Documentation/devicetree/bindings/arm/msm/imem.txt index 2989fbfe7972..440628d02630 100644 --- a/Documentation/devicetree/bindings/arm/msm/imem.txt +++ b/Documentation/devicetree/bindings/arm/msm/imem.txt @@ -73,6 +73,11 @@ USB Diag Cookies: Memory region used to store USB PID and serial numbers to be used by bootloader in download mode. +SSR Minidump Offset +------------------- +-Compatible: "qcom,msm-imem-minidump" +-reg: start address and size of ssr imem region + Required properties: -compatible: "qcom,msm-imem-diag-dload" -reg: start address and size of USB Diag download mode region in imem @@ -121,4 +126,9 @@ Example: compatible = "qcom,msm-imem-emergency_download_mode"; reg = <0xfe0 12>; }; + + ss_mdump@b88 { + compatible = "qcom,msm-imem-minidump"; + reg = <0xb88 28>; + }; }; diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt index 41cbb91351bb..c264f9f65265 100644 --- a/Documentation/devicetree/bindings/arm/msm/msm.txt +++ b/Documentation/devicetree/bindings/arm/msm/msm.txt @@ -110,6 +110,12 @@ SoCs: - SDA630 compatible = "qcom,sda630" +- SDM636 + compatible = "qcom,sdm636" + +- SDA636 + compatible = "qcom,sda636" + - MSM8952 compatible = "qcom,msm8952" @@ -169,6 +175,9 @@ Generic board variants: - RUMI device: compatible = "qcom,rumi" +- SVR device: + compatible = "qcom,svr" + Boards (SoC type + board variant): @@ -202,6 +211,7 @@ compatible = "qcom,apqtitanium-mtp" compatible = "qcom,apq8098-cdp" compatible = "qcom,apq8098-mtp" compatible = "qcom,apq8098-qrd" +compatible = "qcom,apq8098-svr" compatible = "qcom,mdm9630-cdp" compatible = "qcom,mdm9630-mtp" compatible = "qcom,mdm9630-sim" @@ -291,6 +301,12 @@ compatible = "qcom,sda658-mtp" compatible = "qcom,sda658-cdp" compatible = "qcom,sda660-mtp" compatible = "qcom,sda660-cdp" +compatible = "qcom,sdm636-cdp" +compatible = "qcom,sdm636-mtp" +compatible = "qcom,sdm636-qrd" +compatible = "qcom,sda636-qrd" +compatible = "qcom,sda636-mtp" +compatible = "qcom,sda636-cdp" compatible = "qcom,sdm630-rumi" compatible = "qcom,sdm630-mtp" compatible = "qcom,sdm630-cdp" diff --git a/Documentation/devicetree/bindings/arm/msm/rdbg-smp2p.txt b/Documentation/devicetree/bindings/arm/msm/rdbg-smp2p.txt index ce2d8bd54e43..1114308f9436 100644 --- a/Documentation/devicetree/bindings/arm/msm/rdbg-smp2p.txt +++ b/Documentation/devicetree/bindings/arm/msm/rdbg-smp2p.txt @@ -2,12 +2,15 @@ Qualcomm Technologies, Inc. Remote Debugger (RDBG) driver Required properties: -compatible : Should be one of - To communicate with modem + To communicate with adsp qcom,smp2pgpio_client_rdbg_2_in (inbound) qcom,smp2pgpio_client_rdbg_2_out (outbound) To communicate with modem qcom,smp2pgpio_client_rdbg_1_in (inbound) qcom,smp2pgpio_client_rdbg_1_out (outbound) + To communicate with cdsp + qcom,smp2pgpio_client_rdbg_5_in (inbound) + qcom,smp2pgpio_client_rdbg_5_out (outbound) -gpios : the relevant gpio pins of the entry. Example: diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt index e14acdc6303e..1583da81c090 100644 --- a/Documentation/devicetree/bindings/display/msm/sde.txt +++ b/Documentation/devicetree/bindings/display/msm/sde.txt @@ -169,6 +169,7 @@ Optional properties: e.g. qcom,sde-sspp-vig-blocks -- qcom,sde-vig-csc-off: offset of CSC hardware -- qcom,sde-vig-qseed-off: offset of QSEED hardware + -- qcom,sde-vig-qseed-size: A u32 address range for qseed scaler. -- qcom,sde-vig-pcc: offset and version of PCC hardware -- qcom,sde-vig-hsic: offset and version of global PA adjustment -- qcom,sde-vig-memcolor: offset and version of PA memcolor hardware @@ -178,6 +179,7 @@ Optional properties: indicates that the SSPP RGB contains that feature hardware. e.g. qcom,sde-sspp-vig-blocks -- qcom,sde-rgb-scaler-off: offset of RGB scaler hardware + -- qcom,sde-rgb-scaler-size: A u32 address range for scaler. -- qcom,sde-rgb-pcc: offset and version of PCC hardware - qcom,sde-dspp-blocks: A node that lists the blocks inside the DSPP hardware. The block entries will contain the offset and version of each @@ -417,6 +419,7 @@ Example: qcom,sde-sspp-vig-blocks { qcom,sde-vig-csc-off = <0x320>; qcom,sde-vig-qseed-off = <0x200>; + qcom,sde-vig-qseed-size = <0x74>; /* Offset from vig top, version of HSIC */ qcom,sde-vig-hsic = <0x200 0x00010000>; qcom,sde-vig-memcolor = <0x200 0x00010000>; @@ -425,6 +428,7 @@ Example: qcom,sde-sspp-rgb-blocks { qcom,sde-rgb-scaler-off = <0x200>; + qcom,sde-rgb-scaler-size = <0x74>; qcom,sde-rgb-pcc = <0x380 0x00010000>; }; diff --git a/Documentation/devicetree/bindings/drm/msm/mdp.txt b/Documentation/devicetree/bindings/drm/msm/mdp.txt index 3a6db0553fe3..a76b604445bd 100644 --- a/Documentation/devicetree/bindings/drm/msm/mdp.txt +++ b/Documentation/devicetree/bindings/drm/msm/mdp.txt @@ -3,6 +3,7 @@ Qualcomm Technologies,Inc. Adreno/Snapdragon display controller Required properties: Optional properties: +- contiguous-region: reserved memory for HDMI and DSI buffer. - qcom,sde-plane-id-map: plane id mapping for virtual plane. - qcom,sde-plane-id: each virtual plane mapping node. - reg: reg property. @@ -17,6 +18,8 @@ Optional properties: Example: &mdss_mdp { + contiguous-region = <&cont_splash_mem &cont_splash_mem_hdmi>; + qcom,sde-plane-id-map { qcom,sde-plane-id@0 { reg = <0x0>; diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index d8c3a7c35465..80813dd1b3e5 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -93,6 +93,7 @@ Optional Properties: - qcom,chipid: If it exists this property is used to replace the chip identification read from the GPU hardware. This is used to override faulty hardware readings. +- qcom,disable-wake-on-touch: Boolean. Disables the GPU power up on a touch input event. - qcom,disable-busy-time-burst: Boolean. Disables the busy time burst to avoid switching of power level for large frames based on the busy time limit. @@ -141,6 +142,9 @@ Optional Properties: rendering thread is running on masked CPUs. Bit 0 is for CPU-0, bit 1 is for CPU-1... +- qcom,l2pc-update-queue: + Disables L2PC on masked CPUs at queue time when it's true. + - qcom,snapshot-size: Specify the size of snapshot in bytes. This will override snapshot size defined in the driver code. diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt index 1e6aac56c44e..42e97f765bee 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt @@ -78,6 +78,8 @@ Optional properties for WLED: - qcom,lcd-psm-ctrl : A boolean property to specify if PSM needs to be controlled dynamically when WLED module is enabled or disabled. +- qcom,auto-calibration-enable : A boolean property which enables auto-calibration + of the WLED sink configuration. Optional properties if 'qcom,disp-type-amoled' is mentioned in DT: - qcom,loop-comp-res-kohm : control to select the compensation resistor in kohm. default is 320. diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt index 9fb84020add7..bb413af4b54d 100644 --- a/Documentation/devicetree/bindings/media/video/msm-cci.txt +++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt @@ -123,6 +123,9 @@ Optional properties: - qcom,gpio-vdig : should contain index to gpio used by sensors digital vreg enable - qcom,gpio-vaf : should contain index to gpio used by sensors af vreg enable - qcom,gpio-af-pwdm : should contain index to gpio used by sensors af pwdm_n +- qcom,gpio-custom1 : should contain index to gpio used by sensors specific to usecase +- qcom,gpio-custom2 : should contain index to gpio used by sensors specific to usecase +- qcom,gpio-custom3 : should contain index to gpio used by sensors specific to usecase - qcom,gpio-req-tbl-num : should contain index to gpios specific to this sensor - qcom,gpio-req-tbl-flags : should contain direction of gpios present in qcom,gpio-req-tbl-num property (in the same order) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index d00e26b4d5ed..9916c34e62b8 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -80,6 +80,7 @@ Optional Properties: register dumps on CRC errors and also downgrade bus speed mode to SDR50/DDR50 in case of continuous CRC errors. Set this flag to enable this workaround. + - qcom,force-sdhc1-probe: Force probing sdhc1 even if it is not the boot device. In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage). - qcom,<supply>-always-on - specifies whether supply should be kept "on" always. diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt index fc019bda50a7..bf3ad8a71c26 100644 --- a/Documentation/devicetree/bindings/pci/msm_pcie.txt +++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt @@ -97,6 +97,9 @@ Optional Properties: and assign for each endpoint. - qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become stable after power on, before de-assert the PERST to the endpoint. + - qcom,switch-latency: The time (unit: ms) to wait for the PCIe endpoint's link + training with switch downstream port after the link between switch upstream + port and RC is up. - qcom,wr-halt-size: With base 2, this exponent determines the size of the data that PCIe core will halt on for each write transaction. - qcom,cpl-timeout: Completion timeout value. This value specifies the time range @@ -276,6 +279,7 @@ Example: qcom,smmu-exist; qcom,smmu-sid-base = <0x1480>; qcom,ep-latency = <100>; + qcom,switch-latency = <100>; qcom,wr-halt-size = <0xa>; /* 1KB */ qcom,cpl-timeout = <0x2>; diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt index 47a6fdd300ca..406920b7246e 100644 --- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt +++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt @@ -88,6 +88,7 @@ Optional properties: - qcom,override-acc-1: Override the default ACC settings with this value if present. - qcom,cx-ipeak-vote: Boolean- Present if we need to set bit 5 of cxip_lm_vote_clear during modem shutdown +- qcom,minidump-id: Unique id for each subsystem One child node to represent the MBA image may be specified, when the MBA image needs to be loaded in a specifically carved out memory region. diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8998-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,msm8998-pinctrl.txt new file mode 100644 index 000000000000..8a9514153172 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8998-pinctrl.txt @@ -0,0 +1,201 @@ +Qualcomm Technologies, Inc. MSM8998 TLMM block + +This binding describes the Top Level Mode Multiplexer block found in the +MSM8998 platform. With new GPIOs tiling, GPIO pins are +grouped into various cores - NORTH, WEST, EAST. TLMM_GPIO_ID_STATUSn +register's value for a GPIO pin decides the core location for it. + +- compatible: + Usage: required + Value type: <string> + Definition: must be "qcom,msm8998-pinctrl" + +- reg: + Usage: required + Value type: <prop-encoded-array> + Definition: the base address and size of the TLMM register space. + +- interrupts: + Usage: required + Value type: <prop-encoded-array> + Definition: should specify the TLMM summary IRQ. + +- interrupt-controller: + Usage: required + Value type: <none> + Definition: identifies this node as an interrupt controller + +- #interrupt-cells: + Usage: required + Value type: <u32> + Definition: must be 2. Specifying the pin number and flags, as defined + in <dt-bindings/interrupt-controller/irq.h> + +- gpio-controller: + Usage: required + Value type: <none> + Definition: identifies this node as a gpio controller + +- #gpio-cells: + Usage: required + Value type: <u32> + Definition: must be 2. Specifying the pin number and flags, as defined + in <dt-bindings/gpio/gpio.h> + +Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for +a general description of GPIO and interrupt bindings. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +The pin configuration nodes act as a container for an arbitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as pull-up, drive strength, etc. + + +PIN CONFIGURATION NODES: + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Each subnode only affects those parameters that are explicitly listed. In +other words, a subnode that lists a mux function but no pin configuration +parameters implies no information about any pin configuration parameters. +Similarly, a pin subnode that describes a pullup parameter implies no +information about e.g. the mux function. + + +The following generic properties as defined in pinctrl-bindings.txt are valid +to specify in a pin configuration subnode: + +- pins: + Usage: required + Value type: <string-array> + Definition: List of gpio pins affected by the properties specified in + this subnode. Valid pins are: + gpio0-gpio149, + sdc1_clk, + sdc1_cmd, + sdc1_data + sdc2_clk, + sdc2_cmd, + sdc2_data + sdc1_rclk, + +- function: + Usage: required + Value type: <string> + Definition: Specify the alternative function to be configured for the + specified pins. Functions are only valid for gpio pins. + Valid values are: + + blsp_uart1, blsp_spi1, blsp_i2c1, blsp_uim1, atest_tsens, + bimc_dte1, dac_calib0, blsp_spi8, blsp_uart8, blsp_uim8, + qdss_cti_trig_out_b, bimc_dte0, dac_calib1, qdss_cti_trig_in_b, + dac_calib2, atest_tsens2, atest_usb1, blsp_spi10, blsp_uart10, + blsp_uim10, atest_bbrx1, atest_usb13, atest_bbrx0, atest_usb12, + mdp_vsync, edp_lcd, blsp_i2c10, atest_gpsadc1, atest_usb11, + atest_gpsadc0, edp_hot, atest_usb10, m_voc, dac_gpio, atest_char, + cam_mclk, pll_bypassnl, qdss_stm7, blsp_i2c8, qdss_tracedata_b, + pll_reset, qdss_stm6, qdss_stm5, qdss_stm4, atest_usb2, cci_i2c, + qdss_stm3, dac_calib3, atest_usb23, atest_char3, dac_calib4, + qdss_stm2, atest_usb22, atest_char2, qdss_stm1, dac_calib5, + atest_usb21, atest_char1, dbg_out, qdss_stm0, dac_calib6, + atest_usb20, atest_char0, dac_calib10, qdss_stm10, + qdss_cti_trig_in_a, cci_timer4, blsp_spi6, blsp_uart6, blsp_uim6, + blsp2_spi, qdss_stm9, qdss_cti_trig_out_a, dac_calib11, + qdss_stm8, cci_timer0, qdss_stm13, dac_calib7, cci_timer1, + qdss_stm12, dac_calib8, cci_timer2, blsp1_spi, qdss_stm11, + dac_calib9, cci_timer3, cci_async, dac_calib12, blsp_i2c6, + qdss_tracectl_a, dac_calib13, qdss_traceclk_a, dac_calib14, + dac_calib15, hdmi_rcv, dac_calib16, hdmi_cec, pwr_modem, + dac_calib17, hdmi_ddc, pwr_nav, dac_calib18, pwr_crypto, + dac_calib19, hdmi_hot, dac_calib20, dac_calib21, pci_e0, + dac_calib22, dac_calib23, dac_calib24, tsif1_sync, dac_calib25, + sd_write, tsif1_error, blsp_spi2, blsp_uart2, blsp_uim2, + qdss_cti, blsp_i2c2, blsp_spi3, blsp_uart3, blsp_uim3, blsp_i2c3, + uim3, blsp_spi9, blsp_uart9, blsp_uim9, blsp10_spi, blsp_i2c9, + blsp_spi7, blsp_uart7, blsp_uim7, qdss_tracedata_a, blsp_i2c7, + qua_mi2s, gcc_gp1_clk_a, ssc_irq, uim4, blsp_spi11, blsp_uart11, + blsp_uim11, gcc_gp2_clk_a, gcc_gp3_clk_a, blsp_i2c11, cri_trng0, + cri_trng1, cri_trng, qdss_stm18, pri_mi2s, qdss_stm17, blsp_spi4, + blsp_uart4, blsp_uim4, qdss_stm16, qdss_stm15, blsp_i2c4, + qdss_stm14, dac_calib26, spkr_i2s, audio_ref, lpass_slimbus, + isense_dbg, tsense_pwm1, tsense_pwm2, btfm_slimbus, ter_mi2s, + qdss_stm22, qdss_stm21, qdss_stm20, qdss_stm19, gcc_gp1_clk_b, + sec_mi2s, blsp_spi5, blsp_uart5, blsp_uim5, gcc_gp2_clk_b, + gcc_gp3_clk_b, blsp_i2c5, blsp_spi12, blsp_uart12, blsp_uim12, + qdss_stm25, qdss_stm31, blsp_i2c12, qdss_stm30, qdss_stm29, + tsif1_clk, qdss_stm28, tsif1_en, tsif1_data, sdc4_cmd, qdss_stm27, + qdss_traceclk_b, tsif2_error, sdc43, vfr_1, qdss_stm26, tsif2_clk, + sdc4_clk, qdss_stm24, tsif2_en, sdc42, qdss_stm23, qdss_tracectl_b, + sd_card, tsif2_data, sdc41, tsif2_sync, sdc40, mdp_vsync_p_b, + ldo_en, mdp_vsync_s_b, ldo_update, blsp11_uart_tx_b, blsp11_uart_rx_b, + blsp11_i2c_sda_b, prng_rosc, blsp11_i2c_scl_b, uim2, uim1, uim_batt, + pci_e2, pa_indicator, adsp_ext, ddr_bist, qdss_tracedata_11, + qdss_tracedata_12, modem_tsync, nav_dr, nav_pps, pci_e1, gsm_tx, + qspi_cs, ssbi2, ssbi1, mss_lte, qspi_clk, qspi0, qspi1, qspi2, qspi3, + gpio + +- bias-disable: + Usage: optional + Value type: <none> + Definition: The specified pins should be configued as no pull. + +- bias-pull-down: + Usage: optional + Value type: <none> + Definition: The specified pins should be configued as pull down. + +- bias-pull-up: + Usage: optional + Value type: <none> + Definition: The specified pins should be configued as pull up. + +- output-high: + Usage: optional + Value type: <none> + Definition: The specified pins are configured in output mode, driven + high. + Not valid for sdc pins. + +- output-low: + Usage: optional + Value type: <none> + Definition: The specified pins are configured in output mode, driven + low. + Not valid for sdc pins. + +- drive-strength: + Usage: optional + Value type: <u32> + Definition: Selects the drive strength for the specified pins, in mA. + Valid values are: 2, 4, 6, 8, 10, 12, 14 and 16 + +Example: + + tlmm: pinctrl@01010000 { + compatible = "qcom,msm8998-pinctrl"; + reg = <0x01010000 0x300000>; + interrupts = <0 208 0>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + + uart_console_active: uart_console_active { + mux { + pins = "gpio4", "gpio5"; + function = "blsp_uart8"; + }; + + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-disable; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt index babc4523a29a..dd14890123e6 100644 --- a/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt +++ b/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt @@ -9,6 +9,8 @@ Required properties: Optional property: - qcom,fab-id-valid: Use this property when support to read Fab identification from REV ID peripheral is available. +- qcom,tp-rev-valid: Use this property when support to read TP + revision identification from REV ID peripheral. Example: qcom,revid@100 { diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qnovo.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qnovo.txt index 96b7dd517231..8f35e56816ce 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qnovo.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qnovo.txt @@ -20,6 +20,7 @@ Required properties: Optional Properties: - qcom,external-rsense: To indicate whether the platform uses external or internal rsense for measuring battery current. +- qcom,enable-for-dc: To enable qnovo for dc charging path. Example: diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt index 468db388b0a6..f01eae10bf4f 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt @@ -85,21 +85,6 @@ Charger specific properties: maximum charge current in mA for each thermal level. -- qcom,step-soc-thresholds - Usage: optional - Value type: Array of <u32> - Definition: Array of SOC threshold values, size of 4. This should be a - flat array that denotes the percentage ranging from 0 to 100. - If the array is not present, step charging is disabled. - -- qcom,step-current-deltas - Usage: optional - Value type: Array of <s32> - Definition: Array of delta values for charging current, size of 5, with - FCC as base. This should be a flat array that denotes the - offset of charging current in uA, from -3100000 to 3200000. - If the array is not present, step charging is disabled. - - io-channels Usage: optional Value type: List of <phandle u32> @@ -182,6 +167,22 @@ Charger specific properties: Definition: Specifies the deglitch interval for OTG detection. If the value is not present, 50 msec is used as default. +- qcom,step-charging-enable + Usage: optional + Value type: bool + Definition: Boolean flag which when present enables step-charging. + +- qcom,wd-bark-time-secs + Usage: optional + Value type: <u32> + Definition: WD bark-timeout in seconds. The possible values are + 16, 32, 64, 128. If not defined it defaults to 64. + +- qcom,sw-jeita-enable + Usage: optional + Value type: bool + Definition: Boolean flag which when present enables sw compensation for jeita + ============================================= Second Level Nodes - SMB2 Charger Peripherals ============================================= @@ -217,9 +218,6 @@ pmi8998_charger: qcom,qpnp-smb2 { dpdm-supply = <&qusb_phy0>; - qcom,step-soc-thresholds = <60 70 80 90>; - qcom,step-current-deltas = <500000 250000 150000 0 (-150000)>; - qcom,chgr@1000 { reg = <0x1000 0x100>; interrupts = <0x2 0x10 0x0 IRQ_TYPE_NONE>, diff --git a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt index f419655722d4..376af82381f2 100644 --- a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt +++ b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt @@ -11,6 +11,8 @@ Required properties: Optional properties: - qcom,fastrpc-glink: Flag to use glink instead of smd for IPC +- qcom,fastrpc-vmid-heap-shared: Flag for Dynamic heap feature, to + share HLOS memory buffer to ADSP Optional subnodes: - qcom,msm_fastrpc_compute_cb : Child nodes representing the compute context @@ -25,6 +27,7 @@ Example: qcom,msm_fastrpc { compatible = "qcom,msm-fastrpc-adsp"; qcom,fastrpc-glink; + qcom,fastrpc-vmid-heap-shared; qcom,msm_fastrpc_compute_cb_1 { compatible = "qcom,msm-fastrpc-compute-cb"; diff --git a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt index ed383ce9ea8f..9798ac60b493 100644 --- a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt @@ -209,6 +209,12 @@ Properties below are specific to BOOST subnode only. Definition: Current limit (in mA) of the BOOST rail. Possible values are 200 to 1600mA in 200mA steps. +- qcom,bst-headroom-mv + Usage: optional + Value type: <u16> + Definition: Headroom of the boost (in mV). The minimum headroom is + 200mV and if not specified defaults to 200mV. + ======= Example ======= @@ -250,5 +256,6 @@ pm660l_lcdb: qpnp-lcdb@ec00 { qcom,bst-pd-strength = <1>; qcom,bst-ps = <1>; qcom,bst-ps-threshold-ma = <50>; + qcom,bst-headroom-mv = <200>; }; }; diff --git a/Documentation/devicetree/bindings/soc/qcom/msm-early-cam.txt b/Documentation/devicetree/bindings/soc/qcom/msm-early-cam.txt new file mode 100644 index 000000000000..388426f44524 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/msm-early-cam.txt @@ -0,0 +1,110 @@ +* Qualcomm Technologies Inc MSM BA + +[Root level node] +================== +Required properties: +- compatible: Must be "qcom,early-cam". + +[Subnode] +========== +- qcom,early-cam-input-profile-#: Defines child nodes for the profiles supported + by early camera driver. Each profile should have properties + "mmagic-supply", "gdscr-supply", "vfe0-vdd-supply", + "qcom,cam-vreg-name", "clocks", "clock-names", + "qcom,clock-rates". +Required properties: +- mmagic-supply : should contain mmagic regulator used for mmagic clocks. +- gdscr-supply : should contain gdsr regulator used for cci clocks. +- vfe0-vdd-supply: phandle to vfe0 regulator. +- qcom,cam-vreg-name : name of the voltage regulators required for the device. +- clocks: List of clock handles. The parent clocks of the input clocks to the + devices in this power domain are set to oscclk before power gating + and restored back after powering on a domain. This is required for + all domains which are powered on and off and not required for unused + domains. +- clock-names: name of the clock used by the driver. +- qcom,clock-rates: clock rate in Hz. +Example: + + qcom,early-cam { + cell-index = <0>; + compatible = "qcom,early-cam"; + status = "ok"; + mmagic-supply = <&gdsc_mmagic_camss>; + gdscr-supply = <&gdsc_camss_top>; + vfe0-vdd-supply = <&gdsc_vfe0>; + qcom,cam-vreg-name = "mmagic", "gdscr", "vfe0-vdd"; + clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>, + <&clock_mmss clk_camss_top_ahb_clk>, + <&clock_mmss clk_cci_clk_src>, + <&clock_mmss clk_camss_cci_ahb_clk>, + <&clock_mmss clk_camss_cci_clk>, + <&clock_mmss clk_camss_ahb_clk>, + <&clock_mmss clk_mmagic_camss_axi_clk>, + <&clock_mmss clk_camss_vfe_ahb_clk>, + <&clock_mmss clk_camss_vfe0_ahb_clk>, + <&clock_mmss clk_camss_vfe_axi_clk>, + <&clock_mmss clk_camss_vfe0_stream_clk>, + <&clock_mmss clk_smmu_vfe_axi_clk>, + <&clock_mmss clk_smmu_vfe_ahb_clk>, + <&clock_mmss clk_camss_csi_vfe0_clk>, + <&clock_mmss clk_vfe0_clk_src>, + <&clock_mmss clk_camss_csi_vfe0_clk>, + <&clock_mmss clk_camss_csi2_ahb_clk>, + <&clock_mmss clk_camss_csi2_clk>, + <&clock_mmss clk_camss_csi2phy_clk>, + <&clock_mmss clk_csi2phytimer_clk_src>, + <&clock_mmss clk_camss_csi2phytimer_clk>, + <&clock_mmss clk_camss_csi2rdi_clk>, + <&clock_mmss clk_camss_ispif_ahb_clk>, + <&clock_mmss clk_camss_vfe0_clk>; + clock-names = + "mmss_mmagic_ahb_clk", + "camss_top_ahb_clk", + "cci_clk_src", + "camss_cci_ahb_clk", + "camss_cci_clk", + "camss_ahb_clk", + "mmagic_camss_axi_clk", + "camss_vfe_ahb_clk", + "camss_vfe0_ahb_clk", + "camss_vfe_axi_clk", + "camss_vfe0_stream_clk", + "smmu_vfe_axi_clk", + "smmu_vfe_ahb_clk", + "camss_csi_vfe0_clk", + "vfe0_clk_src", + "camss_csi_vfe0_clk", + "camss_csi2_ahb_clk", + "camss_csi2_clk", + "camss_csi2phy_clk", + "csi2phytimer_clk_src", + "camss_csi2phytimer_clk", + "camss_csi2rdi_clk", + "camss_ispif_ahb_clk", + "clk_camss_vfe0_clk"; + + qcom,clock-rates = <19200000 + 19200000 + 19200000 + 19200000 + 19200000 + 19200000 + 0 + 0 + 0 + 320000000 + 0 + 0 + 0 + 0 + 19200000 + 0 + 0 + 200000000 + 200000000 + 200000000 + 200000000 + 200000000 + 0 + }; diff --git a/Documentation/devicetree/bindings/spi/spidev.txt b/Documentation/devicetree/bindings/spi/spidev.txt new file mode 100644 index 000000000000..886eb4bfadf5 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spidev.txt @@ -0,0 +1,30 @@ +SPIDEV + +SPI slave devices using the spidev driver allowing for userspace +control of the SPI devices. Must be children of a SPI master node +and contain the following properties. + +Required properties: +- compatible: Should contain: + "nxp,mpc57xx" for external CAN controller + "infineon,sli97" for external HSM module + +- reg: Chip select address of device. +- spi-max-frequency: Maximum SPI clocking speed of device in Hz. + +Optional properties: +- spi-cpol: Empty property indicating device requires + inverse clock polarity (CPOL) mode. +- spi-cpha: Empty property indicating device requires + shifted clock phase (CPHA) mode. + +Other optional properties described in +Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: + + spi@0 { + compatible = "nxp,mpc57xx"; + reg = <0>; + spi-max-frequency = <19200000>; + }; diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt index 88ad24900f28..9b40d44d363b 100644 --- a/Documentation/devicetree/bindings/usb/msm-phy.txt +++ b/Documentation/devicetree/bindings/usb/msm-phy.txt @@ -214,6 +214,8 @@ Optional properties: - qcom,major-rev: provide major revision number to differentiate power up sequence. default is 2.0 - qcom,vdda33-voltage-level: A list of three integer values (min, op, max) representing specific voltages (in microvolts) used for the vdda33 supply. + - qcom,tune2-efuse-correction: The value to be adjusted from fused value for + improved rise/fall times. Example: qusb_phy: qusb@f9b39000 { diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 91412a10bf65..e3bed3a961a3 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -21,6 +21,7 @@ amlogic Amlogic, Inc. ampire Ampire Co., Ltd. ams AMS AG amstaos AMS-Taos Inc. +android Google Inc. apm Applied Micro Circuits Corporation (APM) aptina Aptina Imaging arasan Arasan Chip Systems @@ -114,6 +115,7 @@ ibm International Business Machines (IBM) idt Integrated Device Technologies, Inc. iom Iomega Corporation img Imagination Technologies Ltd. +infineon Infineon Technologies AG ingenic Ingenic Semiconductor innolux Innolux Corporation intel Intel Corporation diff --git a/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt b/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt index 061c1d16ad24..d0855115b6d1 100644 --- a/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt +++ b/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt @@ -12,7 +12,7 @@ Required properties: "riva_ccu_base", "pronto_a2xb_base", "pronto_ccpu_base", "pronto_saw2_base", "wlan_tx_phy_aborts","wlan_brdg_err_source", "wlan_tx_status", "alarms_txctl", "alarms_tactl", - "pronto_mcu_base". + "pronto_mcu_base", "pronto_qfuse". - interupts: Pronto to Apps interrupts for tx done and rx pending. - qcom,pronto-vddmx-supply: regulator to supply pronto pll. - qcom,pronto-vddcx-supply: voltage corner regulator to supply WLAN/BT/FM @@ -29,7 +29,7 @@ Required properties: - qcom,wcnss-vadc: VADC handle for battery voltage notification APIs. - pinctrl-<n> : Pinctrl states as described in bindings/pinctrl/pinctrl-bindings.txt - pinctrl-names : Names corresponding to the numbered pinctrl states -- clocks: from common clock binding: handle to xo and rf_clk clocks. +- clocks: from common clock binding: handle to xo, rf_clk and wcnss snoc clocks. - clock-names: Names of all the clocks that are accessed by the subsystem - qcom,vdd-voltage-level: This property represents (nominal, min, max) voltage for iris and pronto regulators in milli-volts. @@ -39,11 +39,16 @@ iris and pronto regulators in micro-amps. Optional properties: - qcom,has-autodetect-xo: boolean flag to determine whether Iris XO auto detect should be performed during boot up. +- qcom,snoc-wcnss-clock-freq: indicates the wcnss snoc clock frequency in Hz. +If wcnss_snoc clock is specified in the list of clocks, this property needs +to be set to make it functional. - qcom,wlan-rx-buff-count: WLAN RX buffer count is a configurable value, using a smaller count for this buffer will reduce the memory usage. - qcom,is-pronto-v3: boolean flag to determine the pronto hardware version in use. subsequently correct workqueue will be used by DXE engine to push frames in TX data path. +- qcom,is-dual-band-disable: boolean flag to determine the WLAN dual band + capability. - qcom,is-pronto-vadc: boolean flag to determine Battery voltage feature support for pronto hardware. - qcom,wcnss-pm : <Core rail LDO#, PA rail LDO#, XO settling time, @@ -94,7 +99,12 @@ Example: clocks = <&clock_rpm clk_xo_wlan_clk>, <&clock_rpm clk_rf_clk2>, <&clock_debug clk_gcc_debug_mux>, - <&clock_gcc clk_wcnss_m_clk>; - clock-names = "xo", "rf_clk", "measure", "wcnss_debug"; + <&clock_gcc clk_wcnss_m_clk>, + <&clock_gcc clk_snoc_wcnss_a_clk>; + + clock-names = "xo", "rf_clk", "measure", "wcnss_debug", + "snoc_wcnss"; + + qcom,snoc-wcnss-clock-freq = <200000000>; qcom,wcnss-pm = <11 21 1200 1 1 6>; }; @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 4 -SUBLEVEL = 76 +SUBLEVEL = 80 EXTRAVERSION = NAME = Blurry Fish Butt @@ -623,6 +623,9 @@ include arch/$(SRCARCH)/Makefile KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,) KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,) KBUILD_CFLAGS += $(call cc-disable-warning,frame-address,) +KBUILD_CFLAGS += $(call cc-disable-warning, format-truncation) +KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow) +KBUILD_CFLAGS += $(call cc-disable-warning, int-in-bool-context) ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE KBUILD_CFLAGS += -Os diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index 96edb3c84425..d1e3b0891a4e 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -17,7 +17,6 @@ CONFIG_AUDIT=y CONFIG_BLK_DEV_INITRD=y CONFIG_CGROUPS=y CONFIG_CGROUP_CPUACCT=y -CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_SCHED=y CONFIG_DEFAULT_SECURITY_SELINUX=y diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts index 5f5e0f3d5b64..27cd4abfc74d 100644 --- a/arch/arm/boot/dts/omap3-n900.dts +++ b/arch/arm/boot/dts/omap3-n900.dts @@ -697,6 +697,8 @@ vmmc_aux-supply = <&vsim>; bus-width = <8>; non-removable; + no-sdio; + no-sd; }; &mmc3 { diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 297d6535382e..faff7ea618d0 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -32,6 +32,7 @@ dtb-$(CONFIG_ARCH_MSM8996) += msm8996-v2-pmi8994-cdp.dtb \ msm8996pro-auto-adp.dtb \ msm8996pro-auto-adp-lite.dtb \ msm8996pro-auto-cdp.dtb \ + msm8996pro-auto-cv2x.dtb \ msm8996pro-pmi8994-cdp.dtb \ msm8996pro-pmi8994-mtp.dtb \ msm8996pro-pmi8994-pmk8001-cdp.dtb \ @@ -168,6 +169,7 @@ dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \ apq8098-v2.1-cdp.dtb \ apq8098-v2.1-qrd.dtb \ apq8098-v2.1-mediabox.dtb \ + apq8098-v2.1-svr20.dtb \ msm8998-v2.1-interposer-sdm660-cdp.dtb \ msm8998-v2.1-interposer-sdm660-mtp.dtb \ msm8998-v2.1-interposer-sdm660-qrd.dtb \ @@ -228,7 +230,34 @@ dtb-$(CONFIG_ARCH_SDM660) += sdm660-sim.dtb \ sda658-rcm.dtb \ sda658-pm660a-mtp.dtb \ sda658-pm660a-cdp.dtb \ - sda658-pm660a-rcm.dtb + sda658-pm660a-rcm.dtb \ + sdm636-cdp.dtb \ + sdm636-mtp.dtb \ + sdm636-qrd.dtb \ + sdm636-rcm.dtb \ + sdm636-headset-jacktype-no-cdp.dtb \ + sdm636-headset-jacktype-no-rcm.dtb \ + sdm636-internal-codec-cdp.dtb \ + sdm636-internal-codec-mtp.dtb \ + sdm636-internal-codec-pm660a-cdp.dtb \ + sdm636-internal-codec-pm660a-mtp.dtb \ + sdm636-internal-codec-pm660a-rcm.dtb \ + sdm636-internal-codec-rcm.dtb \ + sdm636-pm660a-headset-jacktype-no-cdp.dtb \ + sdm636-pm660a-headset-jacktype-no-rcm.dtb \ + sdm636-pm660a-cdp.dtb \ + sdm636-pm660a-mtp.dtb \ + sdm636-pm660a-qrd.dtb \ + sdm636-pm660a-rcm.dtb \ + sdm636-usbc-audio-mtp.dtb \ + sdm636-usbc-audio-rcm.dtb \ + sda636-cdp.dtb \ + sda636-mtp.dtb \ + sda636-rcm.dtb \ + sda636-pm660a-cdp.dtb \ + sda636-pm660a-mtp.dtb \ + sda636-pm660a-qrd-hdk.dtb \ + sda636-pm660a-rcm.dtb dtb-$(CONFIG_ARCH_SDM630) += sdm630-rumi.dtb \ sdm630-pm660a-rumi.dtb \ diff --git a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi index 4081a21b3134..db33594d3827 100644 --- a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi +++ b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi @@ -713,6 +713,10 @@ <&afe_proxy_rx>, <&afe_proxy_tx>, <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, <&incall_music2_rx>, + <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>, + <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>, + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>, + <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>, <&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>, <&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>, <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, @@ -731,6 +735,10 @@ "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770", + "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867", + "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871", + "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866", + "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870", "msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883", "msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887", "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts index 9f8d432a3112..082b04791dbd 100644 --- a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts +++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -42,6 +42,10 @@ i2c@75b6000 { /* BLSP8 */ /* ADV7533 HDMI Bridge Chip removed on ADP Lite */ + adv7533@3d { + status = "disabled"; + }; + adv7533@39 { status = "disabled"; }; @@ -60,6 +64,10 @@ /delete-property/ qcom,dsi-display-active; }; +&sde_kms { + connectors = <&sde_hdmi_tx &sde_hdmi &dsi_adv_7533_1>; +}; + &pil_modem { pinctrl-names = "default"; pinctrl-0 = <&modem_mux>; diff --git a/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts index bd29c0307576..1fa49d8a060d 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts @@ -101,6 +101,10 @@ status = "disabled"; }; +&pcie0 { + qcom,boot-option = <0x0>; +}; + &soc { qcom,msm-dai-mi2s { dai_mi2s3: qcom,msm-dai-q6-mi2s-quat { diff --git a/arch/arm/boot/dts/qcom/apq8098-v2.1-svr20.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-svr20.dts new file mode 100644 index 000000000000..4359a3f38ade --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-svr20.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +#include "apq8098-v2.1.dtsi" +#include "msm8998-svr20.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 SVR V2.0 Board"; + compatible = "qcom,apq8098-svr", "qcom,apq8098", "qcom,svr"; + qcom,board-id = <0x03020008 3>; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi index 5971a3d1025e..aa627b3e7c63 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi @@ -68,7 +68,6 @@ qcom,mdss-dsi-lane-1-state; qcom,mdss-dsi-lane-2-state; qcom,mdss-dsi-lane-3-state; - qcom,cmd-sync-wait-broadcast; qcom,mdss-dsi-panel-timings = [e2 36 24 00 66 6a 28 38 2a 03 04 00]; qcom,mdss-dsi-t-clk-post = <0x0d>; qcom,mdss-dsi-t-clk-pre = <0x2d>; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi index 1a572f97c840..339d87f66d2f 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi @@ -193,7 +193,6 @@ qcom,mdss-dsi-lane-1-state; qcom,mdss-dsi-lane-2-state; qcom,mdss-dsi-lane-3-state; - qcom,cmd-sync-wait-broadcast; qcom,mdss-dsi-panel-timings = [e2 36 24 00 66 6a 28 38 2a 03 04 00]; qcom,mdss-dsi-t-clk-post = <0x0d>; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi index 51a225b82f47..ff3b7b80c449 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -54,6 +54,8 @@ qcom,ulps-enabled; qcom,dcs-cmd-by-left; qcom,mdss-dsi-tx-eot-append; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <121>; qcom,adjust-timer-wakeup-ms = <1>; qcom,mdss-dsi-on-command = [ diff --git a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi index 02c87067f212..933746b8abe7 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -46,6 +46,8 @@ qcom,mdss-dsi-mdp-trigger = "none"; qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 20>; qcom,mdss-dsi-tx-eot-append; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <121>; qcom,adjust-timer-wakeup-ms = <1>; qcom,mdss-dsi-on-command = [ diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-svr-v2-3200mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-svr-v2-3200mah.dtsi new file mode 100644 index 000000000000..048897b084ec --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-svr-v2-3200mah.dtsi @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +qcom,svr835v2_3200mah { + /*3003021_TC_MLP603170_3200mAh_averaged_MasterSlave_Jun292017*/ + qcom, = <24>; + qcom,max-voltage-uv = <4200000>; + qcom,fg-cc-cv-threshold-mv = <4190>; + qcom,fastchg-current-ma = <3200>; + qcom,nom-batt-capacity-mah = <3200>; + qcom,batt-id-kohm = <0>; + qcom,battery-beta = <3435>; + qcom,battery-type = "svr835v2_3200mah"; + qcom,checksum = <0xB7B0>; + qcom,gui-version = "PMI8998GUI - 2.0.0.58"; + qcom,fg-profile-data = [ + 87 16 AB 0B + BE 15 3A 0A + 8B 1C 6D 02 + 76 0D 1F 0A + 50 18 ED 22 + 98 45 CA 52 + 83 00 00 00 + 0D 00 00 00 + 00 00 37 B4 + 78 C5 9D BA + 29 00 08 00 + 3E CA 11 E5 + D4 06 B7 EA + 51 07 0F 02 + 82 DD 22 3B + 1C 06 09 20 + 27 00 14 00 + 1C 19 82 0A + E9 0C 49 03 + 84 1C 5C 03 + D0 15 0D 12 + 91 19 0C 22 + F0 3C 35 4B + 7D 00 00 00 + 12 00 00 00 + 00 00 F3 D4 + 9F B4 AF D3 + 22 00 00 00 + CC EA 11 E5 + 2D F4 35 E3 + A5 F3 49 0B + 8F EA 5A 1A + 9B 33 CC FF + 07 10 00 00 + 21 0D 33 43 + 22 00 40 00 + 07 01 0A FA + FF 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi index 93aeef07cfe0..460e7e76ac4d 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi @@ -24,6 +24,7 @@ compatible = "qcom,qpnp-revid"; reg = <0x100 0x100>; qcom,fab-id-valid; + qcom,tp-rev-valid; }; pm660_misc: qcom,misc@900 { diff --git a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi index 9cd117ce4e0c..075eaef21254 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi @@ -269,6 +269,7 @@ qcom,led-strings-list = [00 01 02]; qcom,loop-auto-gm-en; qcom,pmic-revid = <&pm660l_revid>; + qcom,auto-calibration-enable; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi index 684f6cf9b389..147b537eba33 100644 --- a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi @@ -634,6 +634,7 @@ qcom,en-ext-pfet-sc-pro; qcom,pmic-revid = <&pmi8998_revid>; qcom,loop-auto-gm-en; + qcom,auto-calibration-enable; status = "okay"; }; diff --git a/arch/arm/boot/dts/qcom/msm-rdbg.dtsi b/arch/arm/boot/dts/qcom/msm-rdbg.dtsi index d0c91f9e72ae..6de1a8e2fb7e 100644 --- a/arch/arm/boot/dts/qcom/msm-rdbg.dtsi +++ b/arch/arm/boot/dts/qcom/msm-rdbg.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -72,4 +72,35 @@ compatible = "qcom,smp2pgpio_client_rdbg_1_out"; gpios = <&smp2pgpio_rdbg_1_out 0 0>; }; + + smp2pgpio_rdbg_5_in: qcom,smp2pgpio-rdbg-5-in { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "rdbg"; + qcom,remote-pid = <5>; + qcom,is-inbound; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + qcom,smp2pgpio_client_rdbg_5_in { + compatible = "qcom,smp2pgpio_client_rdbg_5_in"; + gpios = <&smp2pgpio_rdbg_5_in 0 0>; + }; + + smp2pgpio_rdbg_5_out: qcom,smp2pgpio-rdbg-5-out { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "rdbg"; + qcom,remote-pid = <5>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + qcom,smp2pgpio_client_rdbg_5_out { + compatible = "qcom,smp2pgpio_client_rdbg_5_out"; + gpios = <&smp2pgpio_rdbg_5_out 0 0>; + }; }; diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi index 9dbec1e87fda..1283cdddc2db 100644 --- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi @@ -858,6 +858,90 @@ }; &soc { + qcom,early-cam { + cell-index = <0>; + compatible = "qcom,early-cam"; + status = "ok"; + mmagic-supply = <&gdsc_mmagic_camss>; + gdscr-supply = <&gdsc_camss_top>; + vfe0-vdd-supply = <&gdsc_vfe0>; + qcom,cam-vreg-name = "mmagic", "gdscr", "vfe0-vdd"; + clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>, + <&clock_mmss clk_camss_top_ahb_clk>, + <&clock_mmss clk_cci_clk_src>, + <&clock_mmss clk_camss_cci_ahb_clk>, + <&clock_mmss clk_camss_cci_clk>, + <&clock_mmss clk_camss_ahb_clk>, + <&clock_mmss clk_mmagic_camss_axi_clk>, + <&clock_mmss clk_camss_vfe_ahb_clk>, + <&clock_mmss clk_camss_vfe0_ahb_clk>, + <&clock_mmss clk_camss_vfe_axi_clk>, + <&clock_mmss clk_camss_vfe0_stream_clk>, + <&clock_mmss clk_smmu_vfe_axi_clk>, + <&clock_mmss clk_smmu_vfe_ahb_clk>, + <&clock_mmss clk_camss_csi_vfe0_clk>, + <&clock_mmss clk_vfe0_clk_src>, + <&clock_mmss clk_camss_csi_vfe0_clk>, + <&clock_mmss clk_camss_csi2_ahb_clk>, + <&clock_mmss clk_camss_csi2_clk>, + <&clock_mmss clk_camss_csi2phy_clk>, + <&clock_mmss clk_csi2phytimer_clk_src>, + <&clock_mmss clk_camss_csi2phytimer_clk>, + <&clock_mmss clk_camss_csi2rdi_clk>, + <&clock_mmss clk_camss_ispif_ahb_clk>, + <&clock_mmss clk_camss_vfe0_clk>; + clock-names = + "mmss_mmagic_ahb_clk", + "camss_top_ahb_clk", + "cci_clk_src", + "camss_cci_ahb_clk", + "camss_cci_clk", + "camss_ahb_clk", + "mmagic_camss_axi_clk", + "camss_vfe_ahb_clk", + "camss_vfe0_ahb_clk", + "camss_vfe_axi_clk", + "camss_vfe0_stream_clk", + "smmu_vfe_axi_clk", + "smmu_vfe_ahb_clk", + "camss_csi_vfe0_clk", + "vfe0_clk_src", + "camss_csi_vfe0_clk", + "camss_csi2_ahb_clk", + "camss_csi2_clk", + "camss_csi2phy_clk", + "csi2phytimer_clk_src", + "camss_csi2phytimer_clk", + "camss_csi2rdi_clk", + "camss_ispif_ahb_clk", + "clk_camss_vfe0_clk"; + + qcom,clock-rates = <19200000 + 19200000 + 19200000 + 19200000 + 19200000 + 19200000 + 0 + 0 + 0 + 320000000 + 0 + 0 + 0 + 0 + 19200000 + 0 + 0 + 200000000 + 200000000 + 200000000 + 200000000 + 200000000 + 0 + 100000000>; + }; + qcom,ntn_avb { compatible = "qcom,ntn_avb"; @@ -1016,6 +1100,10 @@ <&afe_proxy_rx>, <&afe_proxy_tx>, <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, <&incall_music2_rx>, + <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>, + <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>, + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>, + <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>, <&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>, <&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>, <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, @@ -1034,6 +1122,10 @@ "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770", + "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867", + "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871", + "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866", + "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870", "msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883", "msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887", "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", @@ -1449,11 +1541,21 @@ }; }; +&vfe_smmu { + qcom,no-smr-check; +}; + / { reserved-memory { - lk_mem: lk_pool@91600000 { - reg = <0x0 0x91600000 0x0 0x600000>; + lk_mem: lk_pool@0x91600000 { + no-map; + reg = <0 0x91600000 0 0x00600000>; label = "lk_pool"; }; + + early_camera_mem: early_camera_mem@b3fff000 { + reg = <0 0xb3fff000 0 0x800000>; + label = "early_camera_mem"; + }; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi index ad14bfd00cd1..c3b986786034 100644 --- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi @@ -623,6 +623,90 @@ }; &soc { + qcom,early-cam { + cell-index = <0>; + compatible = "qcom,early-cam"; + status = "ok"; + mmagic-supply = <&gdsc_mmagic_camss>; + gdscr-supply = <&gdsc_camss_top>; + vfe0-vdd-supply = <&gdsc_vfe0>; + qcom,cam-vreg-name = "mmagic", "gdscr", "vfe0-vdd"; + clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>, + <&clock_mmss clk_camss_top_ahb_clk>, + <&clock_mmss clk_cci_clk_src>, + <&clock_mmss clk_camss_cci_ahb_clk>, + <&clock_mmss clk_camss_cci_clk>, + <&clock_mmss clk_camss_ahb_clk>, + <&clock_mmss clk_mmagic_camss_axi_clk>, + <&clock_mmss clk_camss_vfe_ahb_clk>, + <&clock_mmss clk_camss_vfe0_ahb_clk>, + <&clock_mmss clk_camss_vfe_axi_clk>, + <&clock_mmss clk_camss_vfe0_stream_clk>, + <&clock_mmss clk_smmu_vfe_axi_clk>, + <&clock_mmss clk_smmu_vfe_ahb_clk>, + <&clock_mmss clk_camss_csi_vfe0_clk>, + <&clock_mmss clk_vfe0_clk_src>, + <&clock_mmss clk_camss_csi_vfe0_clk>, + <&clock_mmss clk_camss_csi2_ahb_clk>, + <&clock_mmss clk_camss_csi2_clk>, + <&clock_mmss clk_camss_csi2phy_clk>, + <&clock_mmss clk_csi2phytimer_clk_src>, + <&clock_mmss clk_camss_csi2phytimer_clk>, + <&clock_mmss clk_camss_csi2rdi_clk>, + <&clock_mmss clk_camss_ispif_ahb_clk>, + <&clock_mmss clk_camss_vfe0_clk>; + clock-names = + "mmss_mmagic_ahb_clk", + "camss_top_ahb_clk", + "cci_clk_src", + "camss_cci_ahb_clk", + "camss_cci_clk", + "camss_ahb_clk", + "mmagic_camss_axi_clk", + "camss_vfe_ahb_clk", + "camss_vfe0_ahb_clk", + "camss_vfe_axi_clk", + "camss_vfe0_stream_clk", + "smmu_vfe_axi_clk", + "smmu_vfe_ahb_clk", + "camss_csi_vfe0_clk", + "vfe0_clk_src", + "camss_csi_vfe0_clk", + "camss_csi2_ahb_clk", + "camss_csi2_clk", + "camss_csi2phy_clk", + "csi2phytimer_clk_src", + "camss_csi2phytimer_clk", + "camss_csi2rdi_clk", + "camss_ispif_ahb_clk", + "clk_camss_vfe0_clk"; + + qcom,clock-rates = <19200000 + 19200000 + 19200000 + 19200000 + 19200000 + 19200000 + 0 + 0 + 0 + 320000000 + 0 + 0 + 0 + 0 + 19200000 + 0 + 0 + 200000000 + 200000000 + 200000000 + 200000000 + 200000000 + 0 + 100000000>; + }; + ntn1: ntn_avb@1 { /* Neutrno device on RC1*/ compatible = "qcom,ntn_avb"; @@ -838,6 +922,10 @@ <&afe_proxy_rx>, <&afe_proxy_tx>, <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, <&incall_music2_rx>, + <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>, + <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>, + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>, + <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>, <&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>, <&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>, <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, @@ -856,6 +944,10 @@ "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770", + "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867", + "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871", + "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866", + "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870", "msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883", "msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887", "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", @@ -1223,12 +1315,22 @@ /delete-property/ qcom,spkr-sd-n-gpio; }; +&vfe_smmu { + qcom,no-smr-check; +}; + / { reserved-memory { - lk_mem: lk_pool@91600000 { - reg = <0x0 0x91600000 0x0 0x600000>; + lk_mem: lk_pool@0x91600000 { + no-map; + reg = <0 0x91600000 0 0x00600000>; label = "lk_pool"; }; + + early_camera_mem: early_camera_mem@b3fff000 { + reg = <0 0xb3fff000 0 0x800000>; + label = "early_camera_mem"; + }; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8996-cv2x.dtsi b/arch/arm/boot/dts/qcom/msm8996-cv2x.dtsi new file mode 100644 index 000000000000..d18344eb3daf --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996-cv2x.dtsi @@ -0,0 +1,641 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm8996-pinctrl.dtsi" +#include "external-soc.dtsi" + +/ { + reserved-memory { + lk_mem: lk_pool@91600000 { + reg = <0x0 0x91600000 0x0 0x600000>; + label = "lk_pool"; + }; + }; + + aliases { + mhi_rmnet0 = &mhi_rmnet_0; + mhi_rmnet1 = &mhi_rmnet_1; + mhi_uci0 = &mhi_uci; + mhi0 = &mhi; + }; + + gpio-leds { + compatible = "gpio-leds"; + + status { + gpios = <&tlmm 21 0>; + default-state = "on"; + }; + }; + + pps { + compatible = "pps-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&pmx_pps>; + gpios = <&tlmm 22 0>; + status = "okay"; + }; +}; + +&soc { + /delete-node/ sound-9335; + + qcom,ntn_avb { + compatible = "qcom,ntn_avb"; + vdd-ntn-pci-supply = <&ntn_vreg>; + vdd-ntn-io-supply = <&ntn_vreg>; + qcom,ntn-rst-delay-msec = <100>; + qcom,ntn-rc-num = <1>; + qcom,ntn-bus-num = <1>; + qcom,ntn-mdio-bus-id = <1>; + qcom,ntn-phy-addr = <7>; + qcom,msm-bus,name = "ntn"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <100 512 0 0>, + <100 512 207108 14432000>; + }; + + usb_detect: usb_detect { + compatible = "qcom,gpio-usbdetect"; + qcom,vbus-det-gpio = <&pm8994_gpios 17 0>; + interrupt-parent = <&spmi_bus>; + interrupts = <0x0 0x9 0x0 IRQ_TYPE_NONE>; + interrupt-names ="pmic_id_irq"; + }; + + qcom,cnss { + vdd-wlan-en-supply = <&wlan_en_vreg>; + qcom,wlan-en-vreg-support; + /delete-property/ wlan-en-gpio; + /delete-property/ wlan-bootstrap-gpio; + /delete-property/ vdd-wlan-core-supply; + }; + + qcom,cnss_sdio { + compatible = "qcom,cnss_sdio"; + subsys-name = "AR6320_SDIO"; + vdd-wlan-supply = <&rome_vreg>; + vdd-wlan-xtal-supply = <&pm8994_l30>; + vdd-wlan-io-supply = <&pm8994_s4>; + vdd-wlan-dsrc-supply = <&dsrc_vreg>; + qcom,wlan-ramdump-dynamic = <0x200000>; + qcom,skip-wlan-en-toggle; + }; + + ntn_vreg: regulator-ntn-tps65051 { + compatible = "regulator-fixed"; + regulator-name = "ntn_vreg"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <920>; + gpio = <&tlmm 138 0>; + enable-active-high; + }; + + eth_phy_vreg: regulator-eth-phy-tps65051 { + compatible = "regulator-fixed"; + regulator-name = "eth_phy_vreg"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us = <920>; + gpio = <&tlmm 136 0>; + enable-active-high; + regulator-always-on; + }; + + hsm_vreg: regulator-hsm-tps22966 { + compatible = "regulator-fixed"; + regulator-name = "hsm_vreg"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <2410>; + gpio = <&tlmm 81 0>; + enable-active-high; + regulator-always-on; + }; + + dsrc_vreg: dsrc_vreg { + compatible = "regulator-fixed"; + regulator-name = "dsrc_vreg"; + startup-delay-us = <2410>; + enable-active-high; + gpio = <&tlmm 125 0>; + }; + + mhi_rmnet_0: qcom,mhi-rmnet@0 { + compatible = "qcom,mhi-rmnet"; + cell-index = <0>; + qcom,mhi = <&mhi>; + qcom,mhi-rx-channel = <101>; + qcom,mhi-tx-channel = <100>; + qcom,mhi-mru = <0x4000>; + status = "okay"; + }; + + mhi_rmnet_1: qcom,mhi-rmnet@1 { + compatible = "qcom,mhi-rmnet"; + cell-index = <1>; + qcom,mhi = <&mhi>; + qcom,mhi-rx-channel = <47>; + qcom,mhi-tx-channel = <46>; + qcom,mhi-mru = <0x4000>; + qcom,interface-name = "mhi_swip"; + status = "okay"; + }; + + mhi_uci: qcom,mhi-uci { + compatible = "qcom,mhi-uci"; + qcom,mhi-uci-channels = <0 0xffff>, + <1 0x1000>, + <2 0xffff>, + <3 0xffff>, + <10 0xffff>, + <11 0x1000>, + <14 0xffff>, + <15 0x1000>, + <16 0xffff>, + <17 0x1000>, + <18 0xffff>, + <19 0x1000>, + <22 0xffff>, + <23 0x1000>, + <24 0xffff>, + <25 0x1000>, + <32 0xffff>, + <33 0x1000>; + qcom,mhi-uci-ctrlchan = <18>; + qcom,mhi = <&mhi>; + status = "okay"; + }; +}; + +/delete-node/ &sde_kms; +/delete-node/ &sde_dsi0; +/delete-node/ &sde_dsi1; +/delete-node/ &sde_dsi_phy0; +/delete-node/ &sde_dsi_phy1; +/delete-node/ &sde_hdmi_tx; +/delete-node/ &mdss_mdp; +/delete-node/ &mdss_dsi; +/delete-node/ &msm_ext_disp; +/delete-node/ &mdss_hdmi_tx; +/delete-node/ &mdss_rotator; +/delete-node/ &routing; + +&pil_modem { + status = "disabled"; +}; + +&pm8994_l9 { + regulator-always-on; +}; + +&pm8994_l10 { + regulator-always-on; +}; + +&wlan_en_vreg { + status = "okay"; +}; + +&i2c_12 { + status = "disabled"; +}; + +&spi_9 { + status = "okay"; + + /* CAN controller */ + spi@0 { + compatible = "nxp,mpc57xx"; + reg = <0>; + spi-max-frequency = <19200000>; + }; +}; + +&spi_12 { + status = "okay"; + + /* HSM module */ + spi@0 { + compatible = "infineon,sli97"; + reg = <0>; + spi-max-frequency = <4800000>; + }; +}; + +&ufs_ice { + status = "okay"; +}; + +&ufsphy1 { + status = "okay"; +}; + +&ufs1 { + status = "okay"; +}; + +&uartblsp2dm1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&uart_console_active>; +}; + +/* Rome SDIO */ +&sdhc_1 { + reg = <0x7464900 0x500>, <0x7464000 0x800>; + reg-names = "hc_mem", "core_mem"; + + vdd-supply = <&wlan_en_vreg>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on>; + pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off>; + + qcom,clk-rates = <400000 20000000 25000000 50000000 + 96000000 192000000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104"; + + qcom,bus-width = <4>; + qcom,core_3_0v_support; + qcom,nonremovable; + qcom,force-sdhc1-probe; + + status = "okay"; +}; + +/* SDCARD slot */ +&sdhc_2 { + vdd-supply = <&pm8994_l21>; + qcom,vdd-voltage-level = <2950000 2950000>; + qcom,vdd-current-level = <200 800000>; + + vdd-io-supply = <&pm8994_l13>; + qcom,vdd-io-voltage-level = <1800000 2950000>; + qcom,vdd-io-current-level = <200 22000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on + &sdc2_cd_on_sbc>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_cd_off_sbc>; + + qcom,clk-rates = <400000 20000000 25000000 + 50000000 100000000 200000000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104"; + + cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + + status = "okay"; +}; + +&usb2s { + status = "okay"; +}; + +&usb3 { + extcon = <&usb_detect>; + vbus_dwc3-supply = <&usb_otg_switch>; + vdda33-supply = <&pm8994_l24>; + vdda18-supply = <&pm8994_l12>; +}; + +&usb_otg_switch { + gpio = <&pm8994_gpios 11 0>; + enable-active-high; + status = "ok"; + /delete-property/ vin-supply; +}; + +&pcie1 { + qcom,msi-gicm-addr = <0x09bd0040>; + qcom,msi-gicm-base = <0x240>; +}; + +&pcie2 { + /* Enumerate MDM on wake interrupt */ + qcom,boot-option = <0x0>; +}; + +&mdm3 { + pinctrl-names = "mdm_active", "mdm_suspend"; + pinctrl-0 = <&ap2mdm_active &mdm2ap_active>; + pinctrl-1 = <&ap2mdm_sleep &mdm2ap_sleep>; + interrupt-map = <0 &tlmm 108 0x3 + 1 &tlmm 107 0x3 + 2 &tlmm 112 0x3>; + qcom,mdm2ap-errfatal-gpio = <&tlmm 108 0x00>; + qcom,ap2mdm-errfatal-gpio = <&tlmm 109 0x00>; + qcom,mdm2ap-status-gpio = <&tlmm 106 0x00>; + qcom,ap2mdm-status-gpio = <&tlmm 107 0x00>; + qcom,ap2mdm-soft-reset-gpio = <&pm8994_mpps 2 GPIO_ACTIVE_LOW>; + qcom,ap2mdm-vddmin-gpio = <&tlmm 111 0x00>; + qcom,mdm2ap-vddmin-gpio = <&tlmm 112 0x00>; + status = "okay"; +}; + +&mhi { + qcom,mhi-address-window = <0x0 0x80000000 0x1 0xffffffff>; + qcom,pci-dev_id = <0x0302>; + qcom,pci-domain = <2>; + qcom,pci-bus = <1>; + qcom,pci-slot = <0>; + esoc-names = "mdm"; + esoc-0 = <&mdm3>; + qcom,msm-bus,name = "mhi"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <100 512 0 0>, + <100 512 1200000000 650000000>; + mhi-chan-cfg-0 = <0x0 0x80 0x2 0x92>; + mhi-chan-cfg-1 = <0x1 0x80 0x2 0xa2>; + mhi-chan-cfg-2 = <0x2 0x80 0x1 0x91>; + mhi-chan-cfg-3 = <0x3 0x80 0x1 0xa1>; + mhi-chan-cfg-4 = <0x4 0x80 0x2 0x92>; + mhi-chan-cfg-5 = <0x5 0x80 0x2 0xa2>; + mhi-chan-cfg-6 = <0x6 0xa 0x2 0x92>; + mhi-chan-cfg-7 = <0x7 0xa 0x2 0xa2>; + mhi-chan-cfg-10 = <0xa 0x80 0x1 0x92>; + mhi-chan-cfg-11 = <0xb 0x80 0x1 0xa2>; + mhi-chan-cfg-14 = <0xe 0x40 0x1 0x92>; + mhi-chan-cfg-15 = <0xf 0x40 0x1 0xa2>; + mhi-chan-cfg-16 = <0x10 0x40 0x1 0x92>; + mhi-chan-cfg-17 = <0x11 0x40 0x1 0xa2>; + mhi-chan-cfg-18 = <0x12 0x40 0x1 0x92>; + mhi-chan-cfg-19 = <0x13 0x40 0x1 0xa2>; + mhi-chan-cfg-20 = <0x14 0xa 0x2 0x92>; + mhi-chan-cfg-21 = <0x15 0xa 0x2 0xa2>; + mhi-chan-cfg-22 = <0x16 0x40 0x2 0x92>; + mhi-chan-cfg-23 = <0x17 0x40 0x2 0xa2>; + mhi-chan-cfg-24 = <0x18 0xa 0x1 0x91>; + mhi-chan-cfg-25 = <0x19 0xa 0x1 0xa1>; + mhi-chan-cfg-32 = <0x20 0x80 0x2 0x92>; + mhi-chan-cfg-33 = <0x21 0x80 0x2 0xa2>; + mhi-chan-cfg-34 = <0x22 0x80 0x2 0x92>; + mhi-chan-cfg-35 = <0x23 0x80 0x2 0xa2>; + mhi-chan-cfg-46 = <0x2e 0x80 0x2 0x412>; + mhi-chan-cfg-47 = <0x2f 0x80 0x3 0x422>; + mhi-chan-cfg-100 = <0x64 0x80 0x4 0x652>; + mhi-chan-cfg-101 = <0x65 0x80 0x5 0x762>; + mhi-event-rings = <6>; + mhi-event-cfg-0 = <0x80 0x0 0x1 0 1 0x11>; + mhi-event-cfg-1 = <0x80 0x1 0x1 0 1 0x11>; + mhi-event-cfg-2 = <0x80 0x2 0x5 0 2 0x11>; + mhi-event-cfg-3 = <0x100 0x3 0x1 47 1 0x48>; + mhi-event-cfg-4 = <0x100 0x4 0x1 100 1 0x69>; + mhi-event-cfg-5 = <0x100 0x5 0x1 101 1 0x68>; + status = "okay"; +}; + +&tlmm { + /* Set these up as hogs */ + pinctrl-names = "default"; + pinctrl-0 = <&can_reset_gpio>; +}; + +&pm8994_gpios { + gpio@c700 { /* GPIO 8 - WLAN_EN */ + qcom,mode = <1>; /* Digital output*/ + qcom,pull = <4>; /* Pulldown 10uA */ + qcom,vin-sel = <2>; /* VIN2 */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; + + gpio@c800 { /* GPIO 9 - Rome 3.3V control */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* MOS logic */ + qcom,invert = <1>; /* Output high */ + qcom,vin-sel = <0>; /* VPH_PWR */ + qcom,src-sel = <0>; /* Constant */ + qcom,out-strength = <1>; /* High drive strength */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; + + gpio@ca00 { /* GPIO 11 - USB ENB1 (otg switch) */ + qcom,mode = <1>; /* DIGITAL OUT */ + qcom,vin-sel = <2>; /* 1.8 */ + qcom,src-sel = <0>; /* GPIO */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; + + gpio@d000 { /* GPIO 17 - USB1 VBUS detect */ + qcom,mode = <0>; /* Digital Input*/ + qcom,pull = <5>; /* No pull */ + qcom,vin-sel = <2>; /* 1.8 V */ + qcom,src-sel = <0>; /* GPIO */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; + + gpio@d100 { /* GPIO 18 - Rome Sleep Clock */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,invert = <0>; /* Output low initially */ + qcom,vin-sel = <2>; /* VIN 2 */ + qcom,src-sel = <3>; /* Function 2 */ + qcom,out-strength = <2>; /* Medium */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; +}; + +&pm8994_mpps { + mpp@a100 { /* MPP 2 */ + /* MDM PON conrol*/ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,vin-sel = <2>; /* S4 1.8V */ + qcom,src-sel = <0>; /* Constant */ + qcom,master-en = <1>; /* Enable GPIO */ + qcom,invert = <1>; /* Output high */ + status = "okay"; + }; +}; + +&pm8994_vadc { + chan@5 { + label = "vcoin"; + reg = <5>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <1>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; + + chan@7 { + label = "vph_pwr"; + reg = <7>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <1>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; + + chan@73 { + label = "msm_therm"; + reg = <0x73>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@74 { + label = "emmc_therm"; + reg = <0x74>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@75 { + label = "pa_therm0"; + reg = <0x75>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@77 { + label = "pa_therm1"; + reg = <0x77>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@78 { + label = "quiet_therm"; + reg = <0x78>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@7c { + label = "xo_therm_buf"; + reg = <0x7c>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <4>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; +}; + +&pm8994_adc_tm { + chan@73 { + label = "msm_therm"; + reg = <0x73>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,btm-channel-number = <0x48>; + qcom,thermal-node; + }; + + chan@74 { + label = "emmc_therm"; + reg = <0x74>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,btm-channel-number = <0x68>; + qcom,thermal-node; + }; + + chan@75 { + label = "pa_therm0"; + reg = <0x75>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,btm-channel-number = <0x70>; + qcom,thermal-node; + }; + + chan@77 { + label = "pa_therm1"; + reg = <0x77>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,btm-channel-number = <0x78>; + qcom,thermal-node; + }; + + chan@78 { + label = "quiet_therm"; + reg = <0x78>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,btm-channel-number = <0x80>; + qcom,thermal-node; + }; + + chan@7c { + label = "xo_therm_buf"; + reg = <0x7c>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <4>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,btm-channel-number = <0x88>; + qcom,thermal-node; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi index d3ea51268590..c8898ec01992 100644 --- a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi @@ -538,6 +538,10 @@ <&afe_proxy_rx>, <&afe_proxy_tx>, <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, <&incall_music2_rx>, + <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>, + <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>, + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>, + <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>, <&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>, <&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>, <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, @@ -556,6 +560,10 @@ "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770", + "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867", + "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871", + "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866", + "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870", "msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883", "msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887", "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", diff --git a/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi index ff128acb376a..d800fdaae3de 100644 --- a/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1449,7 +1449,7 @@ }; cnss_pins { - cnss_default: cnss_default { + cnss_bootstrap_active: cnss_bootstrap_active { mux { pins = "gpio46"; function = "gpio"; @@ -1458,6 +1458,20 @@ config { pins = "gpio46"; drive-strength = <16>; + output-high; + bias-pull-up; + }; + }; + cnss_bootstrap_sleep: cnss_bootstrap_sleep { + mux { + pins = "gpio46"; + function = "gpio"; + }; + + config { + pins = "gpio46"; + drive-strength = <2>; + output-low; bias-pull-down; }; }; @@ -2717,5 +2731,32 @@ }; }; }; + + pmx_pps: pmx_pps { + mux { + pins = "gpio22"; + function = "gpio"; + }; + + config { + pins = "gpio22"; + drive-strength = <16>; /* 16 mA */ + bias-pull-down; /* pull down */ + }; + }; + + can_reset_gpio: can_reset_gpio { + mux { + pins = "gpio71"; + function = "gpio"; + }; + + config { + pins = "gpio71"; + drive-strength = <2>; + output-high; + bias-pull-up; + }; + }; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi index e1921c3baeb3..1800a31dda9a 100644 --- a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi @@ -1918,6 +1918,13 @@ gpio = <&pm8994_gpios 9 0>; }; + wlan_en_vreg: wlan_en_vreg { + compatible = "regulator-fixed"; + regulator-name = "wlan_en_vreg"; + enable-active-high; + gpio = <&pm8994_gpios 8 0>; + }; + hl7509_en_vreg: hl7509_en_vreg { compatible = "regulator-fixed"; regulator-name = "hl7509_en_vreg"; @@ -1979,5 +1986,6 @@ maxim,vrange-sel = <0>; maxim,soft-start-slew-rate = <5500>; maxim,dvs-slew-rate = <5500>; + status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi index 6d0e5c4cb920..b0688668e667 100644 --- a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi @@ -20,6 +20,8 @@ "vbif_phys", "vbif_nrt_phys"; + contiguous-region = <&cont_splash_mem &cont_splash_mem_hdmi>; + /* clock and supply entries */ clocks = <&clock_mmss clk_mdss_ahb_clk>, <&clock_mmss clk_mdss_axi_clk>, diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index dcd884b4a745..505e325db1f5 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -176,6 +176,31 @@ soc: soc { }; + firmware: firmware { + android { + compatible = "android,firmware"; + fstab { + compatible = "android,fstab"; + vendor { + compatible = "android,vendor"; + dev = "/dev/block/platform/soc/7464900.sdhci/by-name/vendor"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,verify"; + status = "ok"; + }; + system { + compatible = "android,system"; + dev = "/dev/block/platform/soc/7464900.sdhci/by-name/system"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,verify"; + status = "ok"; + }; + }; + }; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -2335,15 +2360,17 @@ qcom,cnss { compatible = "qcom,cnss"; wlan-bootstrap-gpio = <&tlmm 46 0>; - wlan-en-gpio = <&pm8994_gpios 8 0>; + vdd-wlan-en-supply = <&wlan_en_vreg>; vdd-wlan-supply = <&rome_vreg>; vdd-wlan-io-supply = <&pm8994_s4>; vdd-wlan-xtal-supply = <&pm8994_l30>; vdd-wlan-core-supply = <&pm8994_s3>; wlan-ant-switch-supply = <&pm8994_l18_pin_ctrl>; + qcom,wlan-en-vreg-support; qcom,notify-modem-status; - pinctrl-names = "default"; - pinctrl-0 = <&cnss_default>; + pinctrl-names = "bootstrap_active", "bootstrap_sleep"; + pinctrl-0 = <&cnss_bootstrap_active>; + pinctrl-1 = <&cnss_bootstrap_sleep>; qcom,wlan-rc-num = <0>; qcom,wlan-ramdump-dynamic = <0x200000>; @@ -3355,6 +3382,82 @@ }; }; + qcom,msm-dai-tdm-pri-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37120>; + qcom,msm-cpudai-tdm-group-num-ports = <4>; + qcom,msm-cpudai-tdm-group-port-id = <36864 36866 36868 36870>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36864>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36866>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36868>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36870>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-pri-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37121>; + qcom,msm-cpudai-tdm-group-num-ports = <4>; + qcom,msm-cpudai-tdm-group-port-id = <36865 36867 36869 36871>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36865>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36867>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36869>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36871>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + qcom,msm-dai-tdm-sec-tx { compatible = "qcom,msm-dai-tdm"; qcom,msm-cpudai-tdm-group-id = <37137>; diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts index 48d5cb78611b..f5c33063643d 100644 --- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts +++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts @@ -42,6 +42,9 @@ i2c@75b6000 { /* BLSP8 */ /* ADV7533 HDMI Bridge Chip removed on ADP Lite */ + adv7533@3d { + status = "disabled"; + }; adv7533@39 { status = "disabled"; }; diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts index 7f6f3d5d4a4c..d2aa5c854c83 100644 --- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts +++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,7 +22,7 @@ model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO ADP"; compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp"; qcom,msm-id = <315 0x10000>; - qcom,board-id = <0x02010019 0>; + qcom,board-id = <0x02010019 0>, <0x00010001 0>; }; &spi_9 { diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-cv2x.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-cv2x.dts new file mode 100644 index 000000000000..a89aa879436b --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-cv2x.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "msm8996pro.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996pro-auto.dtsi" +#include "msm8996-cv2x.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO CV2X"; + compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp"; + qcom,msm-id = <315 0x10001>; + qcom,board-id = <0x1e 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8996pro.dtsi b/arch/arm/boot/dts/qcom/msm8996pro.dtsi index 252940c9c3e5..b9a2ccb973f2 100644 --- a/arch/arm/boot/dts/qcom/msm8996pro.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996pro.dtsi @@ -22,7 +22,7 @@ qcom,msm-id = <305 0x10000>; chosen { - bootargs = "fpsimd.fpsimd_settings=1 app_setting.use_app_setting=0 app_setting.use_32bit_app_setting_pro=1"; + bootargs = "lpm_levels.sleep_disabled=1 fpsimd.fpsimd_settings=1 app_setting.use_app_setting=0 app_setting.use_32bit_app_setting_pro=1"; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-qrd-vr1.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-qrd-vr1.dtsi index 14567c3b5010..2af3bf277096 100644 --- a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-qrd-vr1.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-qrd-vr1.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -345,6 +345,60 @@ clock-names = "cam_src_clk", "cam_clk"; qcom,clock-rates = <24000000 0>; }; + + /* ToF Camera*/ + qcom,camera@3 { + cell-index = <3>; + compatible = "qcom,camera"; + reg = <0x3>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <3>; + qcom,mount-angle = <90>; + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <1800000>; + qcom,cam-vreg-max-voltage = <1800000>; + qcom,cam-vreg-op-mode = <80000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk3_active + &cam_sensor_depth_v1_active + &cam_sensor_depth_v2_active + &cam_sensor_depth_default>; + pinctrl-1 = <&cam_sensor_mclk3_suspend + &cam_sensor_depth_v1_sleep + &cam_sensor_depth_v2_sleep + &cam_sensor_depth_sleep>; + gpios = <&tlmm 16 0>, + <&tlmm 24 0>, + <&tlmm 21 0>, + <&tlmm 28 0>, + <&tlmm 23 0>, + <&tlmm 7 0>; + qcom,gpio-vana = <1>; + qcom,gpio-custom2 = <2>; + qcom,gpio-reset = <3>; + qcom,gpio-custom3 = <4>; + qcom,gpio-custom1 = <5>; + qcom,gpio-req-tbl-num = <0 1 2 3 4 5>; + qcom,gpio-req-tbl-flags = <1 0 0 0 1 1>; + qcom,gpio-req-tbl-label = + "CAMIF_MCLK3", + "CAM_VANA", + "CAM_CUSTOM2", + "CAM_RESET1", + "CAM_CUSTOM3", + "CAM_CUSTOM1"; + qcom,sensor-position = <1>; /* 0 rear */ + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; /* I2C 1 */ + status = "ok"; + clocks = <&clock_mmss clk_mclk3_clk_src>, + <&clock_mmss clk_mmss_camss_mclk3_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + }; &pm8998_gpios { diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-svr20.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-svr20.dtsi new file mode 100644 index 000000000000..9d408ee5f3a7 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-svr20.dtsi @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + led_flash0: qcom,camera-flash@0 { + cell-index = <0>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>; + qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>; + qcom,switch-source = <&pmi8998_switch0>; + status = "ok"; + }; + + led_flash1: qcom,camera-flash@1 { + cell-index = <1>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash2>; + qcom,torch-source = <&pmi8998_torch2>; + qcom,switch-source = <&pmi8998_switch1>; + status = "ok"; + }; +}; + +&cci { + actuator0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + qcom,cci-master = <0>; + gpios = <&tlmm 27 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + }; + + actuator1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + qcom,cci-master = <1>; + gpios = <&tlmm 27 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + }; + + ois0: qcom,ois@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + qcom,cci-master = <0>; + gpios = <&tlmm 27 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "disabled"; + }; + + eeprom0: qcom,eeprom@0 { + cell-index = <0>; + reg = <0>; + compatible = "qcom,eeprom"; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 80000 105000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk0_active + &cam_sensor_rear_active + &cam_actuator_vaf_active>; + pinctrl-1 = <&cam_sensor_mclk0_suspend + &cam_sensor_rear_suspend + &cam_actuator_vaf_suspend>; + gpios = <&tlmm 13 0>, + <&tlmm 30 0>, + <&pm8998_gpios 20 0>, + <&tlmm 29 0>, + <&tlmm 27 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK0", + "CAM_RESET0", + "CAM_VDIG", + "CAM_VANA", + "CAM_VAF"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk0_clk_src>, + <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + eeprom1: qcom,eeprom@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,eeprom"; + cam_vdig-supply = <&pm8998_lvs1>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-min-voltage = <0 0 3312000>; + qcom,cam-vreg-max-voltage = <0 0 3600000>; + qcom,cam-vreg-op-mode = <0 0 80000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk2_active + &cam_sensor_rear2_active>; + pinctrl-1 = <&cam_sensor_mclk2_suspend + &cam_sensor_rear2_suspend>; + gpios = <&tlmm 15 0>, + <&tlmm 9 0>, + <&tlmm 8 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vana = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK1", + "CAM_RESET1", + "CAM_VANA1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + eeprom2: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; + cam_vio-supply = <&pm8998_lvs1>; + /*cam_vana-supply = <&pm8998_l22>;*/ + cam_vdig-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; + qcom,cam-vreg-min-voltage = + <0 2864000 1352000>; + qcom,cam-vreg-max-voltage = + <0 2864000 1352000>; + qcom,cam-vreg-op-mode = <0 80000 105000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk1_active + &cam_sensor_front_active>; + pinctrl-1 = <&cam_sensor_mclk1_suspend + &cam_sensor_front_suspend>; + gpios = <&tlmm 14 0>, + <&tlmm 28 0>, + <&pm8998_gpios 9 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK2", + "CAM_RESET2", + "CAM_VDIG"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk1_clk_src>, + <&clock_mmss clk_mmss_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,camera"; + reg = <0x0>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <270>; + cam_vio-supply = <&pm8998_l8>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_l9>; + cam_v_custom1-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vdig", "cam_vana", + "cam_vio", "cam_v_custom1"; + qcom,cam-vreg-min-voltage = <1808000 3312000 1200000 0>; + qcom,cam-vreg-max-voltage = <2960000 3600000 1200000 0>; + qcom,cam-vreg-op-mode = <0 80000 105000 0>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk0_active + &cam_sensor_6dofl_active>; + pinctrl-1 = <&cam_sensor_mclk0_suspend + &cam_sensor_6dofl_suspend>; + gpios = <&tlmm 13 0>, + <&tlmm 148 0>, + <&tlmm 93 0>, + <&tlmm 52 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vana = <2>; + qcom,gpio-vdig = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK0", + "CAM_RESET0", + "CAM_VANA", + "CAM_VDIG"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <1>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk0_clk_src>, + <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,camera"; + reg = <0x1>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom1>; + qcom,actuator-src = <&actuator1>; + cam_vdig-supply = <&pm8998_lvs1>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-min-voltage = <0 0 3312000>; + qcom,cam-vreg-max-voltage = <0 0 3600000>; + qcom,cam-vreg-op-mode = <0 0 80000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk2_active + &cam_sensor_rear2_active>; + pinctrl-1 = <&cam_sensor_mclk2_suspend + &cam_sensor_rear2_suspend>; + gpios = <&tlmm 15 0>, + <&tlmm 9 0>, + <&tlmm 8 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vana = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK1", + "CAM_RESET1", + "CAM_VANA1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + qcom,camera@2 { + cell-index = <2>; + compatible = "qcom,camera"; + reg = <0x02>; + qcom,csiphy-sd-index = <2>; + qcom,csid-sd-index = <2>; + qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom2>; + qcom,led-flash-src = <&led_flash1>; + qcom,actuator-src = <&actuator1>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; + qcom,cam-vreg-min-voltage = + <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = + <0 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 80000 105000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk1_active + &cam_sensor_front_active>; + pinctrl-1 = <&cam_sensor_mclk1_suspend + &cam_sensor_front_suspend>; + gpios = <&tlmm 14 0>, + <&tlmm 28 0>, + <&pm8998_gpios 9 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK2", + "CAM_RESET2", + "CAM_VDIG"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "disabled"; + clocks = <&clock_mmss clk_mclk1_clk_src>, + <&clock_mmss clk_mmss_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + qcom,camera@3 { + cell-index = <3>; + compatible = "qcom,camera"; + reg = <0x3>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <270>; + cam_vio-supply = <&pm8998_l8>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_l9>; + cam_v_custom1-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vdig", "cam_vana", + "cam_vio", "cam_v_custom1"; + qcom,cam-vreg-min-voltage = <1808000 3312000 1200000 0>; + qcom,cam-vreg-max-voltage = <2960000 3600000 1200000 0>; + qcom,cam-vreg-op-mode = <0 80000 105000 0>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk0_active + &cam_sensor_6dofr_active>; + pinctrl-1 = <&cam_sensor_mclk0_suspend + &cam_sensor_6dofr_suspend>; + gpios = <&tlmm 13 0>, + <&tlmm 149 0>, + <&tlmm 93 0>, + <&tlmm 52 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vana = <2>; + qcom,gpio-vdig = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK0", + "CAM_RESET1", + "CAM_VANA1", + "CAM_VDIG1"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <1>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk0_clk_src>, + <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + +}; + +&pm8998_gpios { + gpio@c800 { /* GPIO 9 - CAMERA SENSOR 2 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <0>; /* VIN1 GPIO_LV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; + + gpio@d300 { /* GPIO 20 - CAMERA SENSOR 0 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <1>; /* VIN1 GPIO_MV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi index 93b6a7664ed8..897ab12fe0a7 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi @@ -87,7 +87,6 @@ qcom,mdss-dsi-t-clk-post = <0x07>; qcom,mdss-dsi-t-clk-pre = <0x25>; qcom,mdss-dsi-tx-eot-append; - qcom,cmd-sync-wait-broadcast; qcom,esd-check-enabled; qcom,mdss-dsi-min-refresh-rate = <55>; qcom,mdss-dsi-max-refresh-rate = <60>; @@ -107,7 +106,6 @@ qcom,mdss-dsi-t-clk-post = <0x0d>; qcom,mdss-dsi-t-clk-pre = <0x2d>; qcom,mdss-dsi-tx-eot-append; - qcom,cmd-sync-wait-broadcast; qcom,esd-check-enabled; qcom,mdss-dsi-panel-status-check-mode = "reg_read"; qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; diff --git a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi index 71593012148d..ed1259918620 100644 --- a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi @@ -993,6 +993,86 @@ }; }; + cam_sensor_depth_default: cam_sensor_depth_default { + mux { + pins = "gpio28","gpio23","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio28","gpio23","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_depth_sleep: cam_sensor_depth_sleep { + mux { + pins = "gpio28","gpio23","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio28","gpio23","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_depth_v1_active: cam_sensor_depth_v1_active { + /* Depth VANA */ + mux { + pins = "gpio24"; + function = "gpio"; + }; + + config { + pins = "gpio24"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_depth_v1_sleep: cam_sensor_depth_v1_sleep { + mux { + pins = "gpio24"; + function = "gpio"; + }; + + config { + pins = "gpio24"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_depth_v2_active: cam_sensor_depth_v2_active { + /* Depth CUSTOM2 */ + mux { + pins = "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio21"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_depth_v2_sleep: cam_sensor_depth_v2_sleep { + mux { + pins = "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio21"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + cam_sensor_mclk0_active: cam_sensor_mclk0_active { /* MCLK0 */ mux { diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi index 354ac830e0fa..f95ef2b84e2c 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi @@ -14,7 +14,7 @@ sde_kms: qcom,sde_kms@c900000 { compatible = "qcom,sde-kms"; reg = <0x0c900000 0x90000>, - <0x0c9b0000 0x1040>; + <0x0c9b0000 0x2008>; reg-names = "mdp_phys", "vbif_phys"; /* clock and supply entries */ @@ -52,23 +52,44 @@ /* hw blocks */ qcom,sde-off = <0x1000>; + qcom,sde-len = <0x458>; + qcom,sde-ctl-off = <0x2000 0x2200 0x2400 0x2600 0x2800>; + qcom,sde-ctl-size = <0x94>; + qcom,sde-mixer-off = <0x45000 0x46000 0x47000 0x48000 0x49000 0x4a000>; + qcom,sde-mixer-size = <0x31c>; + qcom,sde-dspp-off = <0x55000 0x57000>; + qcom,sde-dspp-size = <0x17e0>; + qcom,sde-wb-off = <0x66000>; + qcom,sde-wb-size = <0x2dc>; + qcom,sde-wb-id = <2>; qcom,sde-wb-xin-id = <6>; qcom,sde-wb-clk-ctrl = <0x2bc 0x10>; qcom,sde-intf-off = <0x6b000 0x6b800 0x6c000 0x6c800>; + qcom,sde-intf-size = <0x280>; + qcom,sde-intf-type = "dp", "dsi", "dsi", "hdmi"; + qcom,sde-pp-off = <0x71000 0x71800 - 0x72000 0x72800>; - qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0>; + 0x72000 0x72800 0x73000>; + qcom,sde-pp-slave = <0x0 0x0 0x0 0x0 0x1>; + + qcom,sde-pp-size = <0xd4>; + + qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0 0x0>; qcom,sde-cdm-off = <0x7a200>; - qcom,sde-dsc-off = <0x10000 0x10000 0x0 0x0>; + qcom,sde-cdm-size = <0x224>; + + qcom,sde-dsc-off = <0x81000 0x81400>; + qcom,sde-dsc-size = <0x140>; + qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>; qcom,sde-sspp-type = "vig", "vig", "vig", "vig", @@ -78,6 +99,7 @@ qcom,sde-sspp-off = <0x5000 0x7000 0x9000 0xb000 0x25000 0x27000 0x29000 0x2b000 0x35000 0x37000>; + qcom,sde-sspp-src-size = <0x1ac>; qcom,sde-sspp-xin-id = <0 4 8 12 1 5 9 13 2 10>; @@ -113,6 +135,7 @@ qcom,sde-sspp-vig-blocks { qcom,sde-vig-csc-off = <0x1a00>; qcom,sde-vig-qseed-off = <0xa00>; + qcom,sde-vig-qseed-size = <0xa0>; }; qcom,platform-supply-entries { diff --git a/arch/arm/boot/dts/qcom/msm8998-svr20-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8998-svr20-pinctrl.dtsi new file mode 100644 index 000000000000..1347fcef4251 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-svr20-pinctrl.dtsi @@ -0,0 +1,3386 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + tlmm: pinctrl@03400000 { + compatible = "qcom,msm8998-pinctrl"; + reg = <0x3400000 0xc00000>; + interrupts = <0 208 0>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + + uart_console_active: uart_console_active { + mux { + pins = "gpio4", "gpio5"; + function = "blsp_uart8_a"; + }; + + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-disable; + }; + }; + + wcd9xxx_intr { + wcd_intr_default: wcd_intr_default{ + mux { + pins = "gpio54"; + function = "gpio"; + }; + + config { + pins = "gpio54"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + input-enable; + }; + }; + }; + + /* I2C CONFIGURATION */ + i2c_1 { + i2c_1_active: i2c_1_active { + mux { + pins = "gpio2", "gpio3"; + function = "blsp_i2c1"; + }; + + config { + pins = "gpio2", "gpio3"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_1_sleep: i2c_1_sleep { + mux { + pins = "gpio2", "gpio3"; + function = "blsp_i2c1"; + }; + + config { + pins = "gpio2", "gpio3"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + i2c_2 { + i2c_2_active: i2c_2_active { + mux { + pins = "gpio32", "gpio33"; + function = "blsp_i2c2"; + }; + + config { + pins = "gpio32", "gpio33"; + drive-strength = <8>; + bias-disable; + }; + }; + + i2c_2_sleep: i2c_2_sleep { + mux { + pins = "gpio32", "gpio33"; + function = "blsp_i2c2"; + }; + + config { + pins = "gpio32", "gpio33"; + drive-strength = <8>; + bias-pull-up; + }; + }; + }; + + i2c_3 { + i2c_3_active: i2c_3_active { + mux { + pins = "gpio47", "gpio48"; + function = "blsp_i2c3"; + }; + + config { + pins = "gpio47", "gpio48"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_3_sleep: i2c_3_sleep { + mux { + pins = "gpio47", "gpio48"; + function = "blsp_i2c3"; + }; + + config { + pins = "gpio47", "gpio48"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + i2c_4 { + i2c_4_active: i2c_4_active { + mux { + pins = "gpio10", "gpio11"; + function = "blsp_i2c4"; + }; + + config { + pins = "gpio10", "gpio11"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_4_sleep: i2c_4_sleep { + mux { + pins = "gpio10", "gpio11"; + function = "blsp_i2c4"; + }; + + config { + pins = "gpio10", "gpio11"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + i2c_5 { + i2c_5_active: i2c_5_active { + mux { + pins = "gpio87", "gpio88"; + function = "blsp_i2c5"; + }; + + config { + pins = "gpio87", "gpio88"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_5_sleep: i2c_5_sleep { + mux { + pins = "gpio87", "gpio88"; + function = "blsp_i2c5"; + }; + + config { + pins = "gpio87", "gpio88"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + i2c_6 { + i2c_6_active: i2c_6_active { + mux { + pins = "gpio43", "gpio44"; + function = "blsp_i2c6"; + }; + + config { + pins = "gpio43", "gpio44"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_6_sleep: i2c_6_sleep { + mux { + pins = "gpio43", "gpio44"; + function = "blsp_i2c6"; + }; + + config { + pins = "gpio43", "gpio44"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + nfc { + nfc_int_active: nfc_int_active { + /* active state */ + mux { + /* GPIO 92 NFC Read Interrupt */ + pins = "gpio92"; + function = "gpio"; + }; + + config { + pins = "gpio92"; + drive-strength = <6>; /* 6 MA */ + bias-pull-up; + }; + }; + + nfc_int_suspend: nfc_int_suspend { + /* sleep state */ + mux { + /* GPIO 92 NFC Read Interrupt */ + pins = "gpio92"; + function = "gpio"; + }; + + config { + pins = "gpio92"; + drive-strength = <6>; /* 6 MA */ + bias-pull-up; + }; + }; + + nfc_enable_active: nfc_enable_active { + /* active state */ + mux { + /* 12: NFC ENABLE 116:ESE Enable */ + pins = "gpio12", "gpio116"; + function = "gpio"; + }; + + config { + pins = "gpio12", "gpio116"; + drive-strength = <6>; /* 6 MA */ + bias-pull-up; + }; + }; + + nfc_enable_suspend: nfc_enable_suspend { + /* sleep state */ + mux { + /* 12: NFC ENABLE 116:ESE Enable */ + pins = "gpio12", "gpio116"; + function = "gpio"; + }; + + config { + pins = "gpio12", "gpio116"; + drive-strength = <6>; /* 6 MA */ + bias-disable; + }; + }; + }; + + i2c_7 { + i2c_7_active: i2c_7_active { + mux { + pins = "gpio55", "gpio56"; + function = "blsp_i2c7"; + }; + + config { + pins = "gpio55", "gpio56"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_7_sleep: i2c_7_sleep { + mux { + pins = "gpio55", "gpio56"; + function = "blsp_i2c7"; + }; + + config { + pins = "gpio55", "gpio56"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + i2c_8 { + i2c_8_active: i2c_8_active { + mux { + pins = "gpio6", "gpio7"; + function = "blsp_i2c8"; + }; + + config { + pins = "gpio6", "gpio7"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_8_sleep: i2c_8_sleep { + mux { + pins = "gpio6", "gpio7"; + function = "blsp_i2c8"; + }; + + config { + pins = "gpio6", "gpio7"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + i2c_9 { + i2c_9_active: i2c_9_active { + mux { + pins = "gpio51", "gpio52"; + function = "blsp_i2c9"; + }; + + config { + pins = "gpio51", "gpio52"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_9_sleep: i2c_9_sleep { + mux { + pins = "gpio51", "gpio52"; + function = "blsp_i2c9"; + }; + + config { + pins = "gpio51", "gpio52"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + i2c_10 { + i2c_10_active: i2c_10_active { + mux { + pins = "gpio67", "gpio68"; + function = "blsp_i2c10"; + }; + + config { + pins = "gpio67", "gpio68"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_10_sleep: i2c_10_sleep { + mux { + pins = "gpio67", "gpio68"; + function = "blsp_i2c10"; + }; + + config { + pins = "gpio67", "gpio68"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + i2c_11 { + i2c_11_active: i2c_11_active { + mux { + pins = "gpio60", "gpio61"; + function = "blsp_i2c11"; + }; + + config { + pins = "gpio60", "gpio61"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_11_sleep: i2c_11_sleep { + mux { + pins = "gpio60", "gpio61"; + function = "blsp_i2c11"; + }; + + config { + pins = "gpio60", "gpio61"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + i2c_12 { + i2c_12_active: i2c_12_active { + mux { + pins = "gpio83", "gpio84"; + function = "blsp_i2c12"; + }; + + config { + pins = "gpio83", "gpio84"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_12_sleep: i2c_12_sleep { + mux { + pins = "gpio83", "gpio84"; + function = "blsp_i2c12"; + }; + + config { + pins = "gpio83", "gpio84"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + /* SPI CONFIGURATION */ + + spi_1 { + spi_1_active: spi_1_active { + mux { + pins = "gpio0", "gpio1", + "gpio2", "gpio3"; + function = "blsp_spi1"; + }; + + config { + pins = "gpio0", "gpio1", + "gpio2", "gpio3"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_1_sleep: spi_1_sleep { + mux { + pins = "gpio0", "gpio1", + "gpio2", "gpio3"; + function = "blsp_spi1"; + }; + + config { + pins = "gpio0", "gpio1", + "gpio2", "gpio3"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_2 { + spi_2_active: spi_2_active { + mux { + pins = "gpio31", "gpio34", + "gpio32", "gpio33"; + function = "blsp_spi2"; + }; + + config { + pins = "gpio31", "gpio34", + "gpio32", "gpio33"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_2_sleep: spi_2_sleep { + mux { + pins = "gpio31", "gpio34", + "gpio32", "gpio33"; + function = "blsp_spi2"; + }; + + config { + pins = "gpio31", "gpio34", + "gpio32", "gpio33"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_3 { + spi_3_active: spi_3_active { + mux { + pins = "gpio45", "gpio46", + "gpio47", "gpio48"; + function = "blsp_spi3"; + }; + + config { + pins = "gpio45", "gpio46", + "gpio47", "gpio48"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_3_sleep: spi_3_sleep { + mux { + pins = "gpio45", "gpio46", + "gpio47", "gpio48"; + function = "blsp_spi3"; + }; + + config { + pins = "gpio45", "gpio46", + "gpio47", "gpio48"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + pcie0 { + pcie0_clkreq_default: pcie0_clkreq_default { + mux { + pins = "gpio36"; + function = "pci_e0"; + }; + + config { + pins = "gpio36"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie0_perst_default: pcie0_perst_default { + mux { + pins = "gpio35"; + function = "gpio"; + }; + + config { + pins = "gpio35"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + pcie0_wake_default: pcie0_wake_default { + mux { + pins = "gpio37"; + function = "gpio"; + }; + + config { + pins = "gpio37"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + + hph_en0_ctrl { + hph_en0_idle: hph_en0_idle { + mux { + pins = "gpio67"; + function = "gpio"; + }; + config { + pins = "gpio67"; + drive-strength = <2>; + bias-pull-down; + output-low; + }; + }; + hph_en0_active: hph_en0_active { + mux { + pins = "gpio67"; + function = "gpio"; + }; + config { + pins = "gpio67"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + }; + + hph_en1_ctrl { + hph_en1_idle: hph_en1_idle { + mux { + pins = "gpio68"; + function = "gpio"; + }; + config { + pins = "gpio68"; + drive-strength = <2>; + bias-pull-down; + output-low; + }; + }; + hph_en1_active: hph_en1_active { + mux { + pins = "gpio68"; + function = "gpio"; + }; + config { + pins = "gpio68"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + }; + + wcd_gnd_mic_swap { + wcd_gnd_mic_swap_idle: wcd_gnd_mic_swap_idle { + mux { + pins = "gpio75"; + function = "gpio"; + }; + config { + pins = "gpio75"; + drive-strength = <2>; + bias-pull-down; + output-low; + }; + }; + wcd_gnd_mic_swap_active: wcd_gnd_mic_swap_active { + mux { + pins = "gpio75"; + function = "gpio"; + }; + config { + pins = "gpio75"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + }; + + /* USB C analog configuration */ + wcd_usbc_analog_en1 { + wcd_usbc_analog_en1_idle: wcd_usbc_ana_en1_idle { + mux { + pins = "gpio59"; + function = "gpio"; + }; + config { + pins = "gpio59"; + drive-strength = <2>; + bias-pull-down; + output-low; + }; + }; + + wcd_usbc_analog_en1_active: wcd_usbc_ana_en1_active { + mux { + pins = "gpio59"; + function = "gpio"; + }; + config { + pins = "gpio59"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + }; + + wcd_usbc_analog_en2n { + wcd_usbc_analog_en2n_idle: wcd_usbc_ana_en2n_idle { + mux { + pins = "gpio60"; + function = "gpio"; + }; + config { + pins = "gpio60"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + + wcd_usbc_analog_en2n_active: wcd_usbc_ana_en2n_active { + mux { + pins = "gpio60"; + function = "gpio"; + }; + config { + pins = "gpio60"; + drive-strength = <2>; + bias-pull-down; + output-low; + }; + }; + }; + + cdc_reset_ctrl { + cdc_reset_sleep: cdc_reset_sleep { + mux { + pins = "gpio116"; + function = "gpio"; + }; + config { + pins = "gpio116"; + drive-strength = <16>; + bias-disable; + output-low; + }; + }; + cdc_reset_active:cdc_reset_active { + mux { + pins = "gpio116"; + function = "gpio"; + }; + config { + pins = "gpio116"; + drive-strength = <16>; + bias-pull-down; + output-high; + }; + }; + }; + + spi_4 { + spi_4_active: spi_4_active { + mux { + pins = "gpio8", "gpio9", + "gpio10", "gpio11"; + function = "blsp_spi4"; + }; + + config { + pins = "gpio8", "gpio9", + "gpio10", "gpio11"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_4_sleep: spi_4_sleep { + mux { + pins = "gpio8", "gpio9", + "gpio10", "gpio11"; + function = "blsp_spi4"; + }; + + config { + pins = "gpio8", "gpio9", + "gpio10", "gpio11"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spkr_1_sd_n { + spkr_1_sd_n_sleep: spkr_1_sd_n_sleep { + mux { + pins = "gpio111"; + function = "gpio"; + }; + config { + pins = "gpio111"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; + input-enable; + }; + }; + spkr_1_sd_n_active: spkr_1_sd_n_active { + mux { + pins = "gpio111"; + function = "gpio"; + }; + config { + pins = "gpio111"; + drive-strength = <16>; /* 16 mA */ + bias-disable; + output-high; + }; + }; + }; + + spi_5 { + spi_5_active: spi_5_active { + mux { + pins = "gpio85", "gpio86", + "gpio87", "gpio88"; + function = "blsp_spi5"; + }; + + config { + pins = "gpio85", "gpio86", + "gpio87", "gpio88"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_5_sleep: spi_5_sleep { + mux { + pins = "gpio85", "gpio86", + "gpio87", "gpio88"; + function = "blsp_spi5"; + }; + + config { + pins = "gpio85", "gpio86", + "gpio87", "gpio88"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spkr_2_sd_n { + spkr_2_sd_n_sleep: spkr_2_sd_n_sleep { + mux { + pins = "gpio112"; + function = "gpio"; + }; + config { + pins = "gpio112"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; + input-enable; + }; + }; + spkr_2_sd_n_active: spkr_2_sd_n_active { + mux { + pins = "gpio112"; + function = "gpio"; + }; + config { + pins = "gpio112"; + drive-strength = <16>; /* 16 mA */ + bias-disable; + output-high; + }; + }; + }; + + cci0_active: cci0_active { + mux { + /* CLK, DATA */ + pins = "gpio17","gpio18"; // Only 2 + function = "cci_i2c"; + }; + + config { + pins = "gpio17","gpio18"; + bias-pull-up; /* PULL UP*/ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cci0_suspend: cci0_suspend { + mux { + /* CLK, DATA */ + pins = "gpio17","gpio18"; + function = "cci_i2c"; + }; + + config { + pins = "gpio17","gpio18"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cci1_active: cci1_active { + mux { + /* CLK, DATA */ + pins = "gpio19","gpio20"; + function = "cci_i2c"; + }; + + config { + pins = "gpio19","gpio20"; + bias-pull-up; /* PULL UP*/ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cci1_suspend: cci1_suspend { + mux { + /* CLK, DATA */ + pins = "gpio19","gpio20"; + function = "cci_i2c"; + }; + + config { + pins = "gpio19","gpio20"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_actuator_vaf_active: cam_actuator_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_actuator_vaf_suspend: cam_actuator_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_mclk0_active: cam_sensor_mclk0_active { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-disable; /* No PULL */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + cam_sensor_mclk0_suspend: cam_sensor_mclk0_suspend { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + cam_sensor_6dofl_active: cam_sensor_6dofl_active { + /* RESET, STANDBY */ + mux { + pins = "gpio148","gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio148","gpio29"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_6dofr_active: cam_sensor_6dofr_active { + /* RESET, STANDBY */ + mux { + pins = "gpio149","gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio149","gpio29"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_6dofl_suspend: cam_sensor_6dofl_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio148","gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio148","gpio29"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_6dofr_suspend: cam_sensor_6dofr_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio149","gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio149","gpio29"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_active: cam_sensor_rear_active { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio29"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + max_volt_active: max_volt_active { + /* RESET */ + mux { + pins = "gpio128", "gpio129", + "gpio130", "gpio133"; + function = "gpio"; + }; + + config { + pins = "gpio128", "gpio129", + "gpio130", "gpio133"; + bias-disable; /* No PULL */ + drive-strength = <8>; /* 2 MA */ + }; + }; + max_volt_suspend: max_volt_suspend { + /* RESET */ + mux { + pins = "gpio128", "gpio129", + "gpio130", "gpio133"; + function = "gpio"; + }; + + config { + pins = "gpio128", "gpio129", + "gpio130", "gpio133"; + bias-disable; /* No PULL */ + drive-strength = <8>; /* 2 MA */ + }; + }; + max_rst_active: max_rst_active { + /* RESET */ + mux { + pins = "gpio30"; + function = "gpio"; + }; + + config { + pins = "gpio30"; + bias-disable; /* No PULL */ + drive-strength = <8>; /* 2 MA */ + }; + }; + max_rst_suspend: max_rst_suspend { + /* RESET */ + mux { + pins = "gpio30"; + function = "gpio"; + }; + + config { + pins = "gpio30"; + bias-disable; /* No PULL */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + spi_6 { + spi_6_active: spi_6_active { + mux { + pins = "gpio41", "gpio42", + "gpio43", "gpio44"; + function = "blsp_spi6"; + }; + + config { + pins = "gpio41", "gpio42", + "gpio43", "gpio44"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_6_sleep: spi_6_sleep { + mux { + pins = "gpio41", "gpio42", + "gpio43", "gpio44"; + function = "blsp_spi6"; + }; + + config { + pins = "gpio41", "gpio42", + "gpio43", "gpio44"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_7 { + spi_7_active: spi_7_active { + mux { + pins = "gpio53", "gpio54", + "gpio55", "gpio56"; + function = "blsp_spi7"; + }; + + config { + pins = "gpio53", "gpio54", + "gpio55", "gpio56"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_7_sleep: spi_7_sleep { + mux { + pins = "gpio53", "gpio54", + "gpio55", "gpio56"; + function = "blsp_spi7"; + }; + + config { + pins = "gpio53", "gpio54", + "gpio55", "gpio56"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_8 { + spi_8_active: spi_8_active { + mux { + pins = "gpio4", "gpio5", + "gpio6", "gpio7"; + function = "blsp_spi8"; + }; + + config { + pins = "gpio4", "gpio5", + "gpio6", "gpio7"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_8_sleep: spi_8_sleep { + mux { + pins = "gpio4", "gpio5", + "gpio6", "gpio7"; + function = "blsp_spi8"; + }; + + config { + pins = "gpio4", "gpio5", + "gpio6", "gpio7"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_9 { + spi_9_active: spi_9_active { + mux { + pins = "gpio49", "gpio50", + "gpio51", "gpio52"; + function = "blsp_spi9"; + }; + + config { + pins = "gpio49", "gpio50", + "gpio51", "gpio52"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_9_sleep: spi_9_sleep { + mux { + pins = "gpio49", "gpio50", + "gpio51", "gpio52"; + function = "blsp_spi9"; + }; + + config { + pins = "gpio49", "gpio50", + "gpio51", "gpio52"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_10 { + spi_10_active: spi_10_active { + mux { + pins = "gpio65", "gpio66", + "gpio67", "gpio68"; + function = "blsp_spi10"; + }; + + config { + pins = "gpio65", "gpio66", + "gpio67", "gpio68"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_10_sleep: spi_10_sleep { + mux { + pins = "gpio65", "gpio66", + "gpio67", "gpio68"; + function = "blsp_spi10"; + }; + + config { + pins = "gpio65", "gpio66", + "gpio67", "gpio68"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_11 { + spi_11_active: spi_11_active { + mux { + pins = "gpio58", "gpio59", + "gpio60", "gpio61"; + function = "blsp_spi11"; + }; + + config { + pins = "gpio58", "gpio59", + "gpio60", "gpio61"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_11_sleep: spi_11_sleep { + mux { + pins = "gpio58", "gpio59", + "gpio60", "gpio61"; + function = "blsp_spi11"; + }; + + config { + pins = "gpio58", "gpio59", + "gpio60", "gpio61"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_12 { + spi_12_active: spi_12_active { + mux { + pins = "gpio81", "gpio82", + "gpio83", "gpio84"; + function = "blsp_spi12"; + }; + + config { + pins = "gpio81", "gpio82", + "gpio83", "gpio84"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_12_sleep: spi_12_sleep { + mux { + pins = "gpio81", "gpio82", + "gpio83", "gpio84"; + function = "blsp_spi12"; + }; + + config { + pins = "gpio81", "gpio82", + "gpio83", "gpio84"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* HS UART CONFIGURATION */ + blsp1_uart1_active: blsp1_uart1_active { + mux { + pins = "gpio0", "gpio1", "gpio2", "gpio3"; + function = "blsp_uart1_a"; + }; + + config { + pins = "gpio0", "gpio1", "gpio2", "gpio3"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart1_sleep: blsp1_uart1_sleep { + mux { + pins = "gpio0", "gpio1", "gpio2", "gpio3"; + function = "gpio"; + }; + + config { + pins = "gpio0", "gpio1", "gpio2", "gpio3"; + drive-strength = <2>; + bias-disable; + }; + }; + + cam_sensor_rear_suspend: cam_sensor_rear_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio29"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_mclk1_active: cam_sensor_mclk1_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_mclk1_suspend: cam_sensor_mclk1_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear2_active: cam_sensor_rear2_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + blsp1_uart2_active: blsp1_uart2_active { + mux { + pins = "gpio31", "gpio34", "gpio33", "gpio32"; + function = "blsp_uart2_a"; + }; + + config { + pins = "gpio31", "gpio34", "gpio33", "gpio32"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart2_sleep: blsp1_uart2_sleep { + mux { + pins = "gpio31", "gpio34", "gpio33", "gpio32"; + function = "gpio"; + }; + + config { + pins = "gpio31", "gpio34", "gpio33", "gpio32"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart3: blsp1_uart3 { + blsp1_uart3_tx_active: blsp1_uart3_tx_active { + mux { + pins = "gpio45"; + function = "blsp_uart3_a"; + }; + + config { + pins = "gpio45"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart3_tx_sleep: blsp1_uart3_tx_sleep { + mux { + pins = "gpio45"; + function = "gpio"; + }; + + config { + pins = "gpio45"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + blsp1_uart3_rxcts_active: blsp1_uart3_rxcts_active { + mux { + pins = "gpio46", "gpio47"; + function = "blsp_uart3_a"; + }; + + config { + pins = "gpio46", "gpio47"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart3_rxcts_sleep: blsp1_uart3_rxcts_sleep { + mux { + pins = "gpio46", "gpio47"; + function = "gpio"; + }; + + config { + pins = "gpio46", "gpio47"; + drive-strength = <2>; + bias-no-pull; + }; + }; + + blsp1_uart3_rfr_active: blsp1_uart3_rfr_active { + mux { + pins = "gpio48"; + function = "blsp_uart3_a"; + }; + + config { + pins = "gpio48"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart3_rfr_sleep: blsp1_uart3_rfr_sleep { + mux { + pins = "gpio48"; + function = "gpio"; + }; + + config { + pins = "gpio48"; + drive-strength = <2>; + bias-no-pull; + }; + }; + }; + + cam_sensor_rear2_suspend: cam_sensor_rear2_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_mclk2_active: cam_sensor_mclk2_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-disable; /* No PULL */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + cam_sensor_mclk2_suspend: cam_sensor_mclk2_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + cam_sensor_mclk3_active: cam_sensor_mclk3_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio16"; + function = "cam_mclk"; + }; + + config { + pins = "gpio16"; + bias-disable; /* No PULL */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + cam_sensor_mclk3_suspend: cam_sensor_mclk3_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio16"; + function = "cam_mclk"; + }; + + config { + pins = "gpio16"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + + cam_sensor_front_active: cam_sensor_front_active { + /* RESET VANA*/ + mux { + pins = "gpio28", "gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio29"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + blsp2_uart1_active: blsp2_uart1_active { + mux { + pins = "gpio53", "gpio54", "gpio55", "gpio56"; + function = "blsp_uart7_a"; + }; + + config { + pins = "gpio53", "gpio54", "gpio55", "gpio56"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp2_uart1_sleep: blsp2_uart1_sleep { + mux { + pins = "gpio53", "gpio54", "gpio55", "gpio56"; + function = "gpio"; + }; + + config { + pins = "gpio53", "gpio54", "gpio55", "gpio56"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp2_uart2_active: blsp2_uart2_active { + mux { + pins = "gpio4", "gpio5", "gpio6", "gpio7"; + function = "blsp_uart8_a"; + }; + + config { + pins = "gpio4", "gpio5", "gpio6", "gpio7"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp2_uart2_sleep: blsp2_uart2_sleep { + mux { + pins = "gpio4", "gpio5", "gpio6", "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio4", "gpio5", "gpio6", "gpio7"; + drive-strength = <2>; + bias-disable; + }; + }; + + cam_sensor_front_suspend: cam_sensor_front_suspend { + /* RESET */ + mux { + pins = "gpio28"; + function = "gpio"; + }; + + config { + pins = "gpio28"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + pmx_mdss: pmx_mdss { + mdss_dsi_active: mdss_dsi_active { + mux { + pins = "gpio94", "gpio97", "gpio51"; + function = "gpio"; + }; + + config { + pins = "gpio94", "gpio97", "gpio51"; + drive-strength = <8>; /* 8 mA */ + bias-disable = <0>; /* no pull */ + }; + }; + mdss_dsi_suspend: mdss_dsi_suspend { + mux { + pins = "gpio94", "gpio97", "gpio51"; + function = "gpio"; + }; + + config { + pins = "gpio94", "gpio97", "gpio51"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + }; + + pmx_mdss_te { + mdss_te_active: mdss_te_active { + mux { + pins = "gpio97"; + function = "mdp_vsync_b"; + }; + config { + pins = "gpio97"; + drive-strength = <2>; /* 8 mA */ + bias-pull-down; /* pull down*/ + }; + }; + + mdss_te_suspend: mdss_te_suspend { + mux { + pins = "gpio97"; + function = "mdp_vsync_b"; + }; + config { + pins = "gpio97"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + }; + + mdss_dp_aux_active: mdss_dp_aux_active { + mux { + pins = "gpio77", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio77", "gpio78"; + bias-disable = <0>; /* no pull */ + drive-strength = <8>; + }; + }; + + mdss_dp_aux_suspend: mdss_dp_aux_suspend { + mux { + pins = "gpio77", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio77", "gpio78"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + mdss_dp_usbplug_cc_active: mdss_dp_usbplug_cc_active { + mux { + pins = "gpio38"; + function = "gpio"; + }; + + config { + pins = "gpio38"; + bias-disable; + drive-strength = <16>; + }; + }; + + mdss_dp_usbplug_cc_suspend: mdss_dp_usbplug_cc_suspend { + mux { + pins = "gpio38"; + function = "gpio"; + }; + + config { + pins = "gpio38"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + mdss_dp_hpd_active: mdss_dp_hpd_active { + mux { + pins = "gpio34"; + function = "edp_hot"; + }; + + config { + pins = "gpio34"; + bias-pull-down; + drive-strength = <16>; + }; + }; + + mdss_dp_hpd_suspend: mdss_dp_hpd_suspend { + mux { + pins = "gpio34"; + function = "edp_hot"; + }; + + config { + pins = "gpio34"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + blsp2_uart3_active: blsp2_uart3_active { + mux { + pins = "gpio49", "gpio50", "gpio51", "gpio52"; + function = "blsp_uart9_a"; + }; + + config { + pins = "gpio49", "gpio50", "gpio51", "gpio52"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp2_uart3_sleep: blsp2_uart3_sleep { + mux { + pins = "gpio49", "gpio50", "gpio51", "gpio52"; + function = "gpio"; + }; + + config { + pins = "gpio49", "gpio50", "gpio51", "gpio52"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* add pingrp for touchscreen */ + pmx_ts_int_active { + ts_int_active: ts_int_active { + mux { + pins = "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio125"; + drive-strength = <8>; + bias-pull-up; + }; + }; + }; + + pmx_ts_int_suspend { + ts_int_suspend1: ts_int_suspend1 { + mux { + pins = "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio125"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + + pmx_ts_reset_active { + ts_reset_active: ts_reset_active { + mux { + pins = "gpio89"; + function = "gpio"; + }; + + config { + pins = "gpio89"; + drive-strength = <8>; + bias-pull-up; + }; + }; + }; + + pmx_ts_reset_suspend { + ts_reset_suspend1: ts_reset_suspend1 { + mux { + pins = "gpio89"; + function = "gpio"; + }; + + config { + pins = "gpio89"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + + pmx_ts_release { + ts_release: ts_release { + mux { + pins = "gpio125", "gpio89"; + function = "gpio"; + }; + + config { + pins = "gpio125", "gpio89"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + + ts_mux { + ts_active: ts_active { + mux { + pins = "gpio89", "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio89", "gpio125"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ts_reset_suspend: ts_reset_suspend { + mux { + pins = "gpio89"; + function = "gpio"; + }; + + config { + pins = "gpio89"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + ts_int_suspend: ts_int_suspend { + mux { + pins = "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio125"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + ufs_dev_reset_assert: ufs_dev_reset_assert { + config { + pins = "ufs_reset"; + bias-pull-down; /* default: pull down */ + /* + * UFS_RESET driver strengths are having + * different values/steps compared to typical + * GPIO drive strengths. + * + * Following table clarifies: + * + * HDRV value | UFS_RESET | Typical GPIO + * (dec) | (mA) | (mA) + * 0 | 0.8 | 2 + * 1 | 1.55 | 4 + * 2 | 2.35 | 6 + * 3 | 3.1 | 8 + * 4 | 3.9 | 10 + * 5 | 4.65 | 12 + * 6 | 5.4 | 14 + * 7 | 6.15 | 16 + * + * POR value for UFS_RESET HDRV is 3 which means + * 3.1mA and we want to use that. Hence just + * specify 8mA to "drive-strength" binding and + * that should result into writing 3 to HDRV + * field. + */ + drive-strength = <8>; /* default: 3.1 mA */ + output-low; /* active low reset */ + }; + }; + + ufs_dev_reset_deassert: ufs_dev_reset_deassert { + config { + pins = "ufs_reset"; + bias-pull-down; /* default: pull down */ + /* + * default: 3.1 mA + * check comments under ufs_dev_reset_assert + */ + drive-strength = <8>; + output-high; /* active low reset */ + }; + }; + + sdc2_clk_on: sdc2_clk_on { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc2_clk_off: sdc2_clk_off { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_cmd_on: sdc2_cmd_on { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_cmd_off: sdc2_cmd_off { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_data_on: sdc2_data_on { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_data_off: sdc2_data_off { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_cd_on: sdc2_cd_on { + mux { + pins = "gpio95"; + function = "gpio"; + }; + + config { + pins = "gpio95"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_cd_off: sdc2_cd_off { + mux { + pins = "gpio95"; + function = "gpio"; + }; + + config { + pins = "gpio95"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + + }; + + led_enable: led_enable { + mux { + pins = "gpio21"; + drive_strength = <16>; + output-high; + }; + }; + + led_disable: led_disable { + mux { + pins = "gpio21"; + drive_strength = <2>; + output-low; + }; + }; + + trigout_a: trigout_a { + mux { + pins = "gpio58"; + function = "qdss_cti1_a"; + }; + + config { + pins = "gpio58"; + drive-strength = <2>; + bias-disable; + }; + }; + + mdss_hdmi_5v_active: mdss_hdmi_5v_active { + mux { + pins = "gpio133"; + function = "gpio"; + }; + + config { + pins = "gpio133"; + bias-pull-up; + drive-strength = <16>; + }; + }; + + mdss_hdmi_5v_suspend: mdss_hdmi_5v_suspend { + mux { + pins = "gpio133"; + function = "gpio"; + }; + + config { + pins = "gpio133"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + + mdss_hdmi_hpd_active: mdss_hdmi_hpd_active { + mux { + pins = "gpio34"; + function = "hdmi_hot"; + }; + + config { + pins = "gpio34"; + bias-pull-down; + drive-strength = <16>; + }; + }; + + mdss_hdmi_hpd_suspend: mdss_hdmi_hpd_suspend { + mux { + pins = "gpio34"; + function = "hdmi_hot"; + }; + + config { + pins = "gpio34"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + mdss_hdmi_ddc_active: mdss_hdmi_ddc_active { + mux { + pins = "gpio32", "gpio33"; + function = "hdmi_ddc"; + }; + + config { + pins = "gpio32", "gpio33"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + mdss_hdmi_ddc_suspend: mdss_hdmi_ddc_suspend { + mux { + pins = "gpio32", "gpio33"; + function = "hdmi_ddc"; + }; + + config { + pins = "gpio32", "gpio33"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + mdss_hdmi_cec_active: mdss_hdmi_cec_active { + mux { + pins = "gpio31"; + function = "hdmi_cec"; + }; + + config { + pins = "gpio31"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + mdss_hdmi_cec_suspend: mdss_hdmi_cec_suspend { + mux { + pins = "gpio31"; + function = "hdmi_cec"; + }; + + config { + pins = "gpio31"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + tsif0_signals_active: tsif0_signals_active { + tsif1_clk { + pins = "gpio89"; /* TSIF0 CLK */ + function = "tsif1_clk"; + }; + tsif1_en { + pins = "gpio90"; /* TSIF0 Enable */ + function = "tsif1_en"; + }; + tsif1_data { + pins = "gpio91"; /* TSIF0 DATA */ + function = "tsif1_data"; + }; + signals_cfg { + pins = "gpio89", "gpio90", "gpio91"; + drive_strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + + /* sync signal is only used if configured to mode-2 */ + tsif0_sync_active: tsif0_sync_active { + tsif1_sync { + pins = "gpio9"; /* TSIF0 SYNC */ + function = "tsif1_sync"; + drive_strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + + tsif1_signals_active: tsif1_signals_active { + tsif2_clk { + pins = "gpio93"; /* TSIF1 CLK */ + function = "tsif2_clk"; + }; + tsif2_en { + pins = "gpio94"; /* TSIF1 Enable */ + function = "tsif2_en"; + }; + tsif2_data { + pins = "gpio95"; /* TSIF1 DATA */ + function = "tsif2_data"; + }; + signals_cfg { + pins = "gpio93", "gpio94", "gpio95"; + drive_strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + + /* sync signal is only used if configured to mode-2 */ + tsif1_sync_active: tsif1_sync_active { + tsif2_sync { + pins = "gpio96"; /* TSIF1 SYNC */ + function = "tsif2_sync"; + drive_strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + + pri_aux_pcm_clk { + pri_aux_pcm_clk_sleep: pri_aux_pcm_clk_sleep { + mux { + pins = "gpio65"; + function = "gpio"; + }; + + config { + pins = "gpio65"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_aux_pcm_clk_active: pri_aux_pcm_clk_active { + mux { + pins = "gpio65"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio65"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_aux_pcm_sync { + pri_aux_pcm_sync_sleep: pri_aux_pcm_sync_sleep { + mux { + pins = "gpio66"; + function = "gpio"; + }; + + config { + pins = "gpio66"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_aux_pcm_sync_active: pri_aux_pcm_sync_active { + mux { + pins = "gpio66"; + function = "pri_mi2s_ws"; + }; + + config { + pins = "gpio66"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_aux_pcm_din { + pri_aux_pcm_din_sleep: pri_aux_pcm_din_sleep { + mux { + pins = "gpio67"; + function = "gpio"; + }; + + config { + pins = "gpio67"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_aux_pcm_din_active: pri_aux_pcm_din_active { + mux { + pins = "gpio67"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio67"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + pri_aux_pcm_dout { + pri_aux_pcm_dout_sleep: pri_aux_pcm_dout_sleep { + mux { + pins = "gpio68"; + function = "gpio"; + }; + + config { + pins = "gpio68"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_aux_pcm_dout_active: pri_aux_pcm_dout_active { + mux { + pins = "gpio68"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio68"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_aux_pcm { + sec_aux_pcm_sleep: sec_aux_pcm_sleep { + mux { + pins = "gpio80", "gpio81"; + function = "gpio"; + }; + + config { + pins = "gpio80", "gpio81"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_aux_pcm_active: sec_aux_pcm_active { + mux { + pins = "gpio80", "gpio81"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio80", "gpio81"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_aux_pcm_din { + sec_aux_pcm_din_sleep: sec_aux_pcm_din_sleep { + mux { + pins = "gpio82"; + function = "gpio"; + }; + + config { + pins = "gpio82"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_aux_pcm_din_active: sec_aux_pcm_din_active { + mux { + pins = "gpio82"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio82"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_aux_pcm_dout { + sec_aux_pcm_dout_sleep: sec_aux_pcm_dout_sleep { + mux { + pins = "gpio83"; + function = "gpio"; + }; + + config { + pins = "gpio83"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_aux_pcm_dout_active: sec_aux_pcm_dout_active { + mux { + pins = "gpio83"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio83"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_aux_pcm { + tert_aux_pcm_sleep: tert_aux_pcm_sleep { + mux { + pins = "gpio75", "gpio76"; + function = "gpio"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_aux_pcm_active: tert_aux_pcm_active { + mux { + pins = "gpio75", "gpio76"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + tert_aux_pcm_din { + tert_aux_pcm_din_sleep: tert_aux_pcm_din_sleep { + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_aux_pcm_din_active: tert_aux_pcm_din_active { + mux { + pins = "gpio77"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio77"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_aux_pcm_dout { + tert_aux_pcm_dout_sleep: tert_aux_pcm_dout_sleep { + mux { + pins = "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio78"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_aux_pcm_dout_active: tert_aux_pcm_dout_active { + mux { + pins = "gpio78"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio78"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_aux_pcm { + quat_aux_pcm_sleep: quat_aux_pcm_sleep { + mux { + pins = "gpio58", "gpio59"; + function = "gpio"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_aux_pcm_active: quat_aux_pcm_active { + mux { + pins = "gpio58", "gpio59"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + quat_aux_pcm_din { + quat_aux_pcm_din_sleep: quat_aux_pcm_din_sleep { + mux { + pins = "gpio60"; + function = "gpio"; + }; + + config { + pins = "gpio60"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_aux_pcm_din_active: quat_aux_pcm_din_active { + mux { + pins = "gpio60"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio60"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_aux_pcm_dout { + quat_aux_pcm_dout_sleep: quat_aux_pcm_dout_sleep { + mux { + pins = "gpio61"; + function = "gpio"; + }; + + config { + pins = "gpio61"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_aux_pcm_dout_active: quat_aux_pcm_dout_active { + mux { + pins = "gpio61"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio61"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + pri_mi2s_mclk { + pri_mi2s_mclk_sleep: pri_mi2s_mclk_sleep { + mux { + pins = "gpio64"; + function = "gpio"; + }; + + config { + pins = "gpio64"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_mclk_active: pri_mi2s_mclk_active { + mux { + pins = "gpio64"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio64"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_mi2s_sck { + pri_mi2s_sck_sleep: pri_mi2s_sck_sleep { + mux { + pins = "gpio65"; + function = "gpio"; + }; + + config { + pins = "gpio65"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_sck_active: pri_mi2s_sck_active { + mux { + pins = "gpio65"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio65"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_mi2s_ws { + pri_mi2s_ws_sleep: pri_mi2s_ws_sleep { + mux { + pins = "gpio66"; + function = "gpio"; + }; + + config { + pins = "gpio66"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_ws_active: pri_mi2s_ws_active { + mux { + pins = "gpio66"; + function = "pri_mi2s_ws"; + }; + + config { + pins = "gpio66"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_mi2s_sd0 { + pri_mi2s_sd0_sleep: pri_mi2s_sd0_sleep { + mux { + pins = "gpio67"; + function = "gpio"; + }; + + config { + pins = "gpio67"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_sd0_active: pri_mi2s_sd0_active { + mux { + pins = "gpio67"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio67"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + pri_mi2s_sd1 { + pri_mi2s_sd1_sleep: pri_mi2s_sd1_sleep { + mux { + pins = "gpio68"; + function = "gpio"; + }; + + config { + pins = "gpio68"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_sd1_active: pri_mi2s_sd1_active { + mux { + pins = "gpio68"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio68"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_mi2s_mclk { + sec_mi2s_mclk_sleep: sec_mi2s_mclk_sleep { + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_mi2s_mclk_active: sec_mi2s_mclk_active { + mux { + pins = "gpio79"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio79"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_mi2s { + sec_mi2s_sleep: sec_mi2s_sleep { + mux { + pins = "gpio80", "gpio81"; + function = "gpio"; + }; + + config { + pins = "gpio80", "gpio81"; + drive-strength = <2>; /* 2 mA */ + bias-disable; /* NO PULL */ + input-enable; + }; + }; + + sec_mi2s_active: sec_mi2s_active { + mux { + pins = "gpio80", "gpio81"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio80", "gpio81"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_mi2s_sd0 { + sec_mi2s_sd0_sleep: sec_mi2s_sd0_sleep { + mux { + pins = "gpio82"; + function = "gpio"; + }; + + config { + pins = "gpio82"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_mi2s_sd0_active: sec_mi2s_sd0_active { + mux { + pins = "gpio82"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio82"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_mi2s_sd1 { + sec_mi2s_sd1_sleep: sec_mi2s_sd1_sleep { + mux { + pins = "gpio83"; + function = "gpio"; + }; + + config { + pins = "gpio83"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_mi2s_sd1_active: sec_mi2s_sd1_active { + mux { + pins = "gpio83"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio83"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_mi2s_mclk { + tert_mi2s_mclk_sleep: tert_mi2s_mclk_sleep { + mux { + pins = "gpio74"; + function = "gpio"; + }; + + config { + pins = "gpio74"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_mi2s_mclk_active: tert_mi2s_mclk_active { + mux { + pins = "gpio74"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio74"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_mi2s { + tert_mi2s_sleep: tert_mi2s_sleep { + mux { + pins = "gpio75", "gpio76"; + function = "gpio"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_mi2s_active: tert_mi2s_active { + mux { + pins = "gpio75", "gpio76"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + tert_mi2s_sd0 { + tert_mi2s_sd0_sleep: tert_mi2s_sd0_sleep { + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_mi2s_sd0_active: tert_mi2s_sd0_active { + mux { + pins = "gpio77"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio77"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_mi2s_sd1 { + tert_mi2s_sd1_sleep: tert_mi2s_sd1_sleep { + mux { + pins = "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio78"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_mi2s_sd1_active: tert_mi2s_sd1_active { + mux { + pins = "gpio78"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio78"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s_mclk { + quat_mi2s_mclk_sleep: quat_mi2s_mclk_sleep { + mux { + pins = "gpio57"; + function = "gpio"; + }; + + config { + pins = "gpio57"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_mclk_active: quat_mi2s_mclk_active { + mux { + pins = "gpio57"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio57"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s { + quat_mi2s_sleep: quat_mi2s_sleep { + mux { + pins = "gpio58", "gpio59"; + function = "gpio"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_active: quat_mi2s_active { + mux { + pins = "gpio58", "gpio59"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + quat_mi2s_sd0 { + quat_mi2s_sd0_sleep: quat_mi2s_sd0_sleep { + mux { + pins = "gpio60"; + function = "gpio"; + }; + + config { + pins = "gpio60"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_sd0_active: quat_mi2s_sd0_active { + mux { + pins = "gpio60"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio60"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s_sd1 { + quat_mi2s_sd1_sleep: quat_mi2s_sd1_sleep { + mux { + pins = "gpio61"; + function = "gpio"; + }; + + config { + pins = "gpio61"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_sd1_active: quat_mi2s_sd1_active { + mux { + pins = "gpio61"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio61"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s_sd2 { + quat_mi2s_sd2_sleep: quat_mi2s_sd2_sleep { + mux { + pins = "gpio62"; + function = "gpio"; + }; + + config { + pins = "gpio62"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_sd2_active: quat_mi2s_sd2_active { + mux { + pins = "gpio62"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio62"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s_sd3 { + quat_mi2s_sd3_sleep: quat_mi2s_sd3_sleep { + mux { + pins = "gpio63"; + function = "gpio"; + }; + + config { + pins = "gpio63"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_sd3_active: quat_mi2s_sd3_active { + mux { + pins = "gpio63"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio63"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + spkr_i2s_clk_pin { + spkr_i2s_clk_sleep: spkr_i2s_clk_sleep { + mux { + pins = "gpio69"; + function = "spkr_i2s"; + }; + + config { + pins = "gpio69"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + + spkr_i2s_clk_active: spkr_i2s_clk_active { + mux { + pins = "gpio69"; + function = "spkr_i2s"; + }; + + config { + pins = "gpio69"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + spkr_1_sd_mediabox { + spkr_1_sd_sleep_mediabox: spkr_1_sd_sleep_mediabox { + mux { + pins = "gpio85"; + function = "gpio"; + }; + config { + pins = "gpio85"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; + input-enable; + }; + }; + spkr_1_sd_active_mediabox: spkr_1_sd_active_mediabox { + mux { + pins = "gpio85"; + function = "gpio"; + }; + config { + pins = "gpio85"; + drive-strength = <8>; /* 8 mA */ + bias-disable; + output-high; + }; + }; + }; + + spkr_2_sd_mediabox_mediabox { + spkr_2_sd_sleep_mediabox: spkr_2_sd_sleep_mediabox { + mux { + pins = "gpio112"; + function = "gpio"; + }; + config { + pins = "gpio112"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; + input-enable; + }; + }; + spkr_2_sd_active_mediabox: spkr_2_sd_active_mediabox { + mux { + pins = "gpio112"; + function = "gpio"; + }; + config { + pins = "gpio112"; + drive-strength = <8>; /* 8 mA */ + bias-disable; + output-high; + }; + }; + }; + + sdc2_cd_on_mediabox: sdc2_cd_on_mediabox { + mux { + pins = "gpio86"; + function = "gpio"; + }; + + config { + pins = "gpio86"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_cd_off_mediabox: sdc2_cd_off_mediabox { + mux { + pins = "gpio86"; + function = "gpio"; + }; + + config { + pins = "gpio86"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + ir_active: ir_active { + mux { + pins = "gpio90", "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio90", "gpio91"; + drive-strength = <16>; + bias-disable; + output-high; + }; + }; + + mtch6102_int { + mtch6102_int_active: mtch6102_int_active { + mux { + pins = "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio125"; + drive-strength = <16>; /* 16 mA */ + bias-pull-up; + }; + }; + + mtch6102_int_suspend: mtch6102_int_suspend { + mux { + pins = "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio125"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; + }; + }; + }; + + mtch6102_rst { + mtch6102_rst_active: mtch6102_rst_active { + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + mtch6102_rst_suspend: mtch6102_rst_suspend { + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-svr20.dtsi b/arch/arm/boot/dts/qcom/msm8998-svr20.dtsi new file mode 100644 index 000000000000..a1fce88c8e54 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-svr20.dtsi @@ -0,0 +1,409 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <dt-bindings/interrupt-controller/irq.h> +#include "msm8998-svr20-pinctrl.dtsi" +#include "msm8998-camera-sensor-svr20.dtsi" +&vendor { + bluetooth: bt_wcn3990 { + compatible = "qca,wcn3990"; + qca,bt-vdd-io-supply = <&pm8998_s3>; + qca,bt-vdd-xtal-supply = <&pm8998_s5>; + qca,bt-vdd-core-supply = <&pm8998_l7>; + qca,bt-vdd-pa-supply = <&pm8998_l17>; + qca,bt-vdd-ldo-supply = <&pm8998_l25>; + qca,bt-chip-pwd-supply = <&pmi8998_bob_pin1>; + clocks = <&clock_gcc clk_rf_clk2_pin>; + clock-names = "rf_clk2"; + + qca,bt-vdd-io-voltage-level = <1352000 1352000>; + qca,bt-vdd-xtal-voltage-level = <2040000 2040000>; + qca,bt-vdd-core-voltage-level = <1800000 1800000>; + qca,bt-vdd-pa-voltage-level = <1304000 1304000>; + qca,bt-vdd-ldo-voltage-level = <3312000 3312000>; + qca,bt-chip-pwd-voltage-level = <3600000 3600000>; + + qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */ + qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */ + qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */ + qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */ + qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */ + }; + svr20_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <25>; + #include "fg-gen3-batterydata-svr-v2-3200mah.dtsi" + }; +}; + +&blue_led { + qcom,default-state = "on"; + linux,default-trigger = "system-running"; +}; + +&pmi8998_charger { + qcom,fcc-max-ua = <5000000>; + qcom,usb-icl-ua = <3000000>; +}; + +&blsp1_uart3_hs { + status = "ok"; +}; + +&ufsphy1 { + vdda-phy-supply = <&pm8998_l1>; + vdda-pll-supply = <&pm8998_l2>; + vddp-ref-clk-supply = <&pm8998_l26>; + vdda-phy-max-microamp = <51400>; + vdda-pll-max-microamp = <14600>; + vddp-ref-clk-max-microamp = <100>; + vddp-ref-clk-always-on; + status = "ok"; +}; + +&ufs1 { + vdd-hba-supply = <&gdsc_ufs>; + vdd-hba-fixed-regulator; + vcc-supply = <&pm8998_l20>; + vccq-supply = <&pm8998_l26>; + vccq2-supply = <&pm8998_s4>; + vcc-max-microamp = <750000>; + vccq-max-microamp = <560000>; + vccq2-max-microamp = <750000>; + status = "ok"; +}; + +&ufs_ice { + status = "ok"; +}; + +&sdhc_2 { + vdd-supply = <&pm8998_l21>; + qcom,vdd-voltage-level = <2950000 2960000>; + qcom,vdd-current-level = <200 800000>; + + vdd-io-supply = <&pm8998_l13>; + qcom,vdd-io-voltage-level = <1808000 2960000>; + qcom,vdd-io-current-level = <200 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>; + + qcom,clk-rates = <400000 20000000 25000000 + 50000000 100000000 200000000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104"; + + cd-gpios = <&tlmm 95 0x1>; + + status = "ok"; +}; + +&uartblsp2dm1 { + status = "ok"; + pinctrl-names = "default"; + pinctrl-0 = <&uart_console_active>; +}; + +&pm8998_gpios { + /* GPIO 5 for Home Key */ + gpio@c400 { + status = "okay"; + qcom,mode = <0>; + qcom,pull = <0>; + qcom,vin-sel = <0>; + qcom,src-sel = <0>; + qcom,out-strength = <1>; + }; + + /* GPIO 6 for Vol+ Key */ + gpio@c500 { + status = "okay"; + qcom,mode = <0>; + qcom,pull = <0>; + qcom,vin-sel = <0>; + qcom,src-sel = <0>; + qcom,out-strength = <1>; + }; + + /* GPIO 7 for Snapshot Key */ + gpio@c600 { + status = "okay"; + qcom,mode = <0>; + qcom,pull = <0>; + qcom,vin-sel = <0>; + qcom,src-sel = <0>; + qcom,out-strength = <1>; + }; + + /* GPIO 8 for Focus Key */ + gpio@c700 { + status = "okay"; + qcom,mode = <0>; + qcom,pull = <0>; + qcom,vin-sel = <0>; + qcom,src-sel = <0>; + qcom,out-strength = <1>; + }; + + gpio@cc00 { /* GPIO 13 */ + qcom,mode = <1>; + qcom,output-type = <0>; + qcom,pull = <5>; + qcom,vin-sel = <0>; + qcom,out-strength = <1>; + qcom,src-sel = <3>; + qcom,master-en = <1>; + status = "okay"; + }; + + /* GPIO 21 (NFC_CLK_REQ) */ + gpio@d400 { + qcom,mode = <0>; + qcom,vin-sel = <1>; + qcom,src-sel = <0>; + qcom,master-en = <1>; + status = "okay"; + }; + + /* GPIO 18 SMB138X */ + gpio@d100 { + qcom,mode = <0>; + qcom,pull = <0>; + qcom,vin-sel = <0>; + qcom,src-sel = <0>; + qcom,master-en = <1>; + status = "okay"; + }; +}; + +&i2c_5 { + status = "okay"; +}; + +&i2c_6 { /* BLSP1 QUP6 (NFC) */ + status = "okay"; + nq@28 { + compatible = "qcom,nq-nci"; + reg = <0x28>; + qcom,nq-irq = <&tlmm 92 0x00>; + qcom,nq-ven = <&tlmm 12 0x00>; + qcom,nq-firm = <&tlmm 93 0x00>; + qcom,nq-clkreq = <&pm8998_gpios 21 0x00>; + qcom,nq-esepwr = <&tlmm 116 0x00>; + interrupt-parent = <&tlmm>; + qcom,clk-src = "BBCLK3"; + interrupts = <92 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active", "nfc_suspend"; + pinctrl-0 = <&nfc_int_active &nfc_enable_active>; + pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>; + clocks = <&clock_gcc clk_ln_bb_clk3_pin>; + clock-names = "ref_clk"; + }; +}; + +&mdss_hdmi_tx { + status = "disabled"; + pinctrl-names = "hdmi_hpd_active", "hdmi_ddc_active", "hdmi_cec_active", + "hdmi_active", "hdmi_sleep"; + pinctrl-0 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active + &mdss_hdmi_ddc_suspend &mdss_hdmi_cec_suspend>; + pinctrl-1 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active + &mdss_hdmi_ddc_active &mdss_hdmi_cec_suspend>; + pinctrl-2 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active + &mdss_hdmi_cec_active &mdss_hdmi_ddc_suspend>; + pinctrl-3 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active + &mdss_hdmi_ddc_active &mdss_hdmi_cec_active>; + pinctrl-4 = <&mdss_hdmi_5v_suspend &mdss_hdmi_hpd_suspend + &mdss_hdmi_ddc_suspend &mdss_hdmi_cec_suspend>; +}; + +&mdss_dp_ctrl { + status = "disabled"; + pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; + pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>; + pinctrl-1 = <&mdss_dp_aux_suspend &mdss_dp_usbplug_cc_suspend>; + qcom,aux-en-gpio = <&tlmm 77 0>; + qcom,aux-sel-gpio = <&tlmm 78 0>; + qcom,usbplug-cc-gpio = <&tlmm 38 0>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "split_dsi"; +}; + +&mem_client_3_size { + qcom,peripheral-size = <0x500000>; +}; + +&pmi8998_haptics { + status = "okay"; +}; + +&pm8998_vadc { + chan@83 { + label = "vph_pwr"; + reg = <0x83>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <1>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; + + chan@85 { + label = "vcoin"; + reg = <0x85>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <1>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; + + chan@4c { + label = "xo_therm"; + reg = <0x4c>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <4>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@4d { + label = "msm_therm"; + reg = <0x4d>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@51 { + label = "quiet_therm"; + reg = <0x51>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; +}; + +&pm8998_adc_tm { + chan@83 { + label = "vph_pwr"; + reg = <0x83>; + qcom,pre-div-channel-scaling = <1>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,btm-channel-number = <0x60>; + }; + + chan@4d { + label = "msm_therm"; + reg = <0x4d>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,btm-channel-number = <0x68>; + qcom,thermal-node; + }; + + chan@51 { + label = "quiet_therm"; + reg = <0x51>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,btm-channel-number = <0x70>; + qcom,thermal-node; + }; + + chan@4c { + label = "xo_therm"; + reg = <0x4c>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <4>; + qcom,hw-settle-time = <2>; + qcom,btm-channel-number = <0x78>; + qcom,thermal-node; + }; +}; + +&wil6210 { + status = "ok"; +}; + +&soc { + gpio_keys { + compatible = "gpio-keys"; + input-name = "gpio-keys"; + status = "okay"; + + home { + label = "home"; + gpios = <&pm8998_gpios 5 0x1>; + linux,input-type = <1>; + linux,code = <158>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + + vol_up { + label = "volume_up"; + gpios = <&pm8998_gpios 6 0x1>; + linux,input-type = <1>; + linux,code = <115>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + + vol_down { + label = "volume_down"; + gpios = <&pm8998_gpios 7 0x1>; + linux,input-type = <1>; + linux,code = <114>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + + confirm { + label = "confirm_key"; + gpios = <&pm8998_gpios 8 0x1>; + linux,input-type = <1>; + linux,code = <28>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + }; +}; + +&pmi8998_fg { + qcom,battery-data = <&svr20_batterydata>; + qcom,fg-force-load-profile; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi index b2f30de94bbc..acdd4bdcd95b 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi @@ -436,7 +436,7 @@ 0x9ac 0x00 0x00 0x8a0 0x01 0x00 0x9e0 0x00 0x00 - 0x9dc 0x01 0x00 + 0x9dc 0x20 0x00 0x9a8 0x00 0x00 0x8a4 0x01 0x00 0x8a8 0x73 0x00 diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 3f8962de22be..eafa6b841c17 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -2675,7 +2675,7 @@ 0x9ac 0x00 0x00 0x8a0 0x01 0x00 0x9e0 0x00 0x00 - 0x9dc 0x01 0x00 + 0x9dc 0x20 0x00 0x9a8 0x00 0x00 0x8a4 0x01 0x00 0x8a8 0x73 0x00 diff --git a/arch/arm/boot/dts/qcom/sda636-cdp.dts b/arch/arm/boot/dts/qcom/sda636-cdp.dts new file mode 100644 index 000000000000..a4cc4b3f4662 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda636-cdp.dts @@ -0,0 +1,36 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sda636.dtsi" +#include "sdm636-cdp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDA 636 PM660 + PM660L CDP"; + compatible = "qcom,sda636-cdp", "qcom,sda636", "qcom,cdp"; + qcom,board-id = <1 0>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; + +&tasha_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; diff --git a/arch/arm/boot/dts/qcom/sda636-mtp.dts b/arch/arm/boot/dts/qcom/sda636-mtp.dts new file mode 100644 index 000000000000..3fd19437b3eb --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda636-mtp.dts @@ -0,0 +1,30 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sda636.dtsi" +#include "sdm636-mtp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDA 636 PM660 + PM660L MTP"; + compatible = "qcom,sda636-mtp", "qcom,sda636", "qcom,mtp"; + qcom,board-id = <8 0>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>; +}; diff --git a/arch/arm/boot/dts/qcom/sda636-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sda636-pm660a-cdp.dts new file mode 100644 index 000000000000..72db1be68c83 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda636-pm660a-cdp.dts @@ -0,0 +1,36 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sda636.dtsi" +#include "sdm636-cdp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDA 636 PM660 + PM660A CDP"; + compatible = "qcom,sda636-cdp", "qcom,sda636", "qcom,cdp"; + qcom,board-id = <1 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; + +&tasha_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; diff --git a/arch/arm/boot/dts/qcom/sda636-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sda636-pm660a-mtp.dts new file mode 100644 index 000000000000..fd5b94420102 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda636-pm660a-mtp.dts @@ -0,0 +1,30 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sda636.dtsi" +#include "sdm636-mtp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDA 636 PM660 + PM660A MTP"; + compatible = "qcom,sda636-mtp", "qcom,sda636", "qcom,mtp"; + qcom,board-id = <8 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>; +}; diff --git a/arch/arm/boot/dts/qcom/sda636-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda636-pm660a-qrd-hdk.dts new file mode 100644 index 000000000000..3535c54f5dd0 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda636-pm660a-qrd-hdk.dts @@ -0,0 +1,240 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sda636.dtsi" +#include "sdm636-qrd.dtsi" +#include "msm-pm660a.dtsi" + +&smb1351_charger { + status = "disabled"; +}; + +&i2c_2 { + smb138x: qcom,smb138x@8 { + compatible = "qcom,i2c-pmic"; + reg = <0x8>; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&tlmm>; + interrupts = <21 IRQ_TYPE_LEVEL_LOW>; + interrupt_names = "smb138x"; + interrupt-controller; + #interrupt-cells = <3>; + qcom,periph-map = <0x10 0x11 0x12 0x13 0x14 0x16 0x36>; + pinctrl-names = "default"; + pinctrl-0 = <&smb_int_default>; + + smb138x_revid: qcom,revid@100 { + compatible = "qcom,qpnp-revid"; + reg = <0x100 0x100>; + }; + + smb138x_tadc: qcom,tadc@3600 { + compatible = "qcom,tadc"; + reg = <0x3600 0x100>; + #address-cells = <1>; + #size-cells = <0>; + #io-channel-cells = <1>; + interrupt-parent = <&smb138x>; + interrupts = <0x36 0x0 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "eoc"; + + batt_temp@0 { + reg = <0>; + qcom,rbias = <68100>; + qcom,rtherm-at-25degc = <68000>; + qcom,beta-coefficient = <3450>; + }; + + skin_temp@1 { + reg = <1>; + qcom,rbias = <33000>; + qcom,rtherm-at-25degc = <68000>; + qcom,beta-coefficient = <3450>; + }; + + die_temp@2 { + reg = <2>; + qcom,scale = <(-1306)>; + qcom,offset = <397904>; + }; + + batt_i@3 { + reg = <3>; + qcom,channel = <3>; + qcom,scale = <(-20000000)>; + }; + + batt_v@4 { + reg = <4>; + qcom,scale = <5000000>; + }; + + input_i@5 { + reg = <5>; + qcom,scale = <14285714>; + }; + + input_v@6 { + reg = <6>; + qcom,scale = <25000000>; + }; + + otg_i@7 { + reg = <7>; + qcom,scale = <5714286>; + }; + }; + + smb138x_parallel_slave: qcom,smb138x-parallel-slave@1000 { + compatible = "qcom,smb138x-parallel-slave"; + qcom,pmic-revid = <&smb138x_revid>; + reg = <0x1000 0x700>; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&smb138x>; + io-channels = + <&smb138x_tadc 1>, + <&smb138x_tadc 2>, + <&smb138x_tadc 3>, + <&smb138x_tadc 14>, + <&smb138x_tadc 15>, + <&smb138x_tadc 16>, + <&smb138x_tadc 17>; + io-channel-names = + "connector_temp", + "charger_temp", + "batt_i", + "connector_temp_thr1", + "connector_temp_thr2", + "connector_temp_thr3", + "charger_temp_max"; + + qcom,chgr@1000 { + reg = <0x1000 0x100>; + interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "chg-state-change"; + }; + + qcom,chgr-misc@1600 { + reg = <0x1600 0x100>; + interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>, + <0x16 0x6 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog-bark", + "temperature-change"; + }; + }; + }; +}; + +&tlmm { + smb_int_default: smb_int_default { + mux { + pins = "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio21"; + drive-strength = <2>; + bias-pull-up; + }; + }; +}; + +/ { + model = "Qualcomm Technologies, Inc. SDA 636 PM660 + PM660A QRD HDK636"; + compatible = "qcom,sda636-qrd", "qcom,sda636", "qcom,qrd"; + qcom,board-id = <0x0016000b 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&pm660a_oledb { + status = "okay"; + qcom,oledb-default-voltage-mv = <6400>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dp_ctrl { + pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; + pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>; + pinctrl-1 = <&mdss_dp_aux_suspend &mdss_dp_usbplug_cc_suspend>; + qcom,aux-en-gpio = <&tlmm 55 0>; + qcom,aux-sel-gpio = <&tlmm 56 0>; + qcom,usbplug-cc-gpio = <&tlmm 58 0>; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; + +&tasha_snd { + qcom,audio-routing = + "AIF4 VI", "MCLK", + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC3", "MIC BIAS3", + "MIC BIAS3", "Digital Mic3", + "DMIC5", "MIC BIAS4", + "MIC BIAS4", "Digital Mic5", + "SpkrLeft IN", "SPK1 OUT"; + qcom,msm-mbhc-hphl-swh = <0>; +}; + +&usb2s { + status = "okay"; +}; + +&qusb_phy0 { + reg = <0x0c012000 0x180>, + <0x00188018 0x4>; + reg-names = "qusb_phy_base", + "ref_clk_addr"; + qcom,qusb-phy-init-seq = <0xf8 0x80 + 0xb3 0x84 + 0x83 0x88 + 0xc7 0x8c + 0x30 0x08 + 0x79 0x0c + 0x21 0x10 + 0x14 0x9c + 0x9f 0x1c + 0x00 0x18>; +}; diff --git a/arch/arm/boot/dts/qcom/sda636-pm660a-rcm.dts b/arch/arm/boot/dts/qcom/sda636-pm660a-rcm.dts new file mode 100644 index 000000000000..321de0526c77 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda636-pm660a-rcm.dts @@ -0,0 +1,36 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sda636.dtsi" +#include "sdm636-cdp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDA 636 PM660 + PM660A RCM"; + compatible = "qcom,sda636-cdp", "qcom,sda636", "qcom,cdp"; + qcom,board-id = <21 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; + +&tasha_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; diff --git a/arch/arm/boot/dts/qcom/sda636-rcm.dts b/arch/arm/boot/dts/qcom/sda636-rcm.dts new file mode 100644 index 000000000000..50cf84bef0e5 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda636-rcm.dts @@ -0,0 +1,36 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sda636.dtsi" +#include "sdm636-cdp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDA 636 PM660 + PM660L RCM"; + compatible = "qcom,sda636-cdp", "qcom,sda636", "qcom,cdp"; + qcom,board-id = <21 0>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; + +&tasha_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; diff --git a/arch/arm/boot/dts/qcom/sda636.dtsi b/arch/arm/boot/dts/qcom/sda636.dtsi new file mode 100644 index 000000000000..a219ca92767a --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda636.dtsi @@ -0,0 +1,29 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "sdm636.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDA 636"; + compatible = "qcom,sda636"; + qcom,msm-id = <346 0x0>; +}; + +&soc { + qcom,rmnet-ipa { + status = "disabled"; + }; +}; + +&ipa_hw { + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts index 604823ff416e..0f4b462fd57b 100644 --- a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts +++ b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts @@ -170,6 +170,15 @@ qcom,mdss-pref-prim-intf = "dsi"; }; +&mdss_dp_ctrl { + pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; + pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>; + pinctrl-1 = <&mdss_dp_aux_suspend &mdss_dp_usbplug_cc_suspend>; + qcom,aux-en-gpio = <&tlmm 55 0>; + qcom,aux-sel-gpio = <&tlmm 56 0>; + qcom,usbplug-cc-gpio = <&tlmm 58 0>; +}; + &mdss_dsi { hw-config = "single_dsi"; }; diff --git a/arch/arm/boot/dts/qcom/sdm630-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm630-camera-sensor-mtp.dtsi index 0275016c9662..32a251f00550 100644 --- a/arch/arm/boot/dts/qcom/sdm630-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-camera-sensor-mtp.dtsi @@ -114,6 +114,26 @@ status = "disabled"; }; + tof0: qcom,tof@29{ + cell-index = <0>; + reg = <0x29>; + compatible = "st,stmvl53l0"; + qcom,cci-master = <0>; + cam_laser-supply = <&pm660l_l8>; + qcom,cam-vreg-name = "cam_laser"; + qcom,cam-vreg-min-voltage = <2800000>; + qcom,cam-vreg-max-voltage = <3400000>; + qcom,cam-vreg-op-mode = <100000>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_tof_active>; + pinctrl-1 = <&cam_tof_suspend>; + stm,irq-gpio = <&tlmm 45 0x2008>; + gpios = <&tlmm 42 0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "RNG_EN"; + }; + eeprom0: qcom,eeprom@0 { cell-index = <0>; reg = <0>; diff --git a/arch/arm/boot/dts/qcom/sdm630-pm.dtsi b/arch/arm/boot/dts/qcom/sdm630-pm.dtsi index f10477bab8cf..f39f8b880690 100644 --- a/arch/arm/boot/dts/qcom/sdm630-pm.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-pm.dtsi @@ -24,7 +24,7 @@ qcom,vctl-timeout-us = <500>; qcom,vctl-port = <0x0>; qcom,phase-port = <0x1>; - qcom,saw2-avs-ctl = <0x101c031>; + qcom,saw2-avs-ctl = <0x1010031>; qcom,saw2-avs-limit = <0x4580458>; qcom,pfm-port = <0x2>; }; @@ -40,7 +40,7 @@ qcom,vctl-timeout-us = <500>; qcom,vctl-port = <0x0>; qcom,phase-port = <0x1>; - qcom,saw2-avs-ctl = <0x101c031>; + qcom,saw2-avs-ctl = <0x1010031>; qcom,saw2-avs-limit = <0x4580458>; qcom,pfm-port = <0x2>; }; diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi index cff3aa809488..e918864a3df7 100644 --- a/arch/arm/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630.dtsi @@ -613,6 +613,7 @@ compatible = "qcom,memshare-peripheral"; qcom,peripheral-size = <0x0>; qcom,client-id = <1>; + qcom,allocate-boot-time; label = "modem"; }; }; @@ -1165,8 +1166,9 @@ < 1113600 762 >, < 1344000 2086 >, < 1670400 2929 >, - < 2150400 3879 >, - < 2380800 4943 >; + < 1881600 3879 >, + < 2150400 4943 >, + < 2380800 5163 >; }; devfreq_memlat_4: qcom,arm-memlat-mon-4 { @@ -1304,18 +1306,9 @@ compatible = "qcom,clk-cpu-osm-sdm630"; reg = <0x179c0000 0x4000>, <0x17916000 0x1000>, <0x17816000 0x1000>, <0x179d1000 0x1000>, - <0x00784130 0x8>, <0x17914800 0x800>, - <0x17814800 0x800>; + <0x00784130 0x8>; reg-names = "osm", "pwrcl_pll", "perfcl_pll", - "apcs_common", "perfcl_efuse", - "pwrcl_acd", "perfcl_acd"; - - qcom,acdtd-val = <0x0000a111 0x0000a111>; - qcom,acdcr-val = <0x002c5ffd 0x002c5ffd>; - qcom,acdsscr-val = <0x00000901 0x00000901>; - qcom,acdextint0-val = <0x2cf9ae8 0x2cf9ae8>; - qcom,acdextint1-val = <0x2cf9afe 0x2cf9afe>; - qcom,acdautoxfer-val = <0x00000015 0x00000015>; + "apcs_common", "perfcl_efuse"; vdd-pwrcl-supply = <&apc0_pwrcl_vreg>; vdd-perfcl-supply = <&apc1_perfcl_vreg>; @@ -1373,7 +1366,6 @@ qcom,up-timer = <1000 1000>; qcom,down-timer = <1000 1000>; - qcom,pc-override-index = <0 0>; qcom,set-ret-inactive; qcom,enable-llm-freq-vote; qcom,llm-freq-up-timer = <327675 327675>; @@ -2039,6 +2031,7 @@ qcom,qsee-ce-hw-instance = <0>; qcom,disk-encrypt-pipe-pair = <2>; qcom,support-fde; + qcom,fde-key-size; qcom,no-clock-support; qcom,msm-bus,name = "qseecom-noc"; qcom,msm-bus,num-cases = <4>; diff --git a/arch/arm/boot/dts/qcom/sdm636-cdp.dts b/arch/arm/boot/dts/qcom/sdm636-cdp.dts new file mode 100644 index 000000000000..47f5eba8f491 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-cdp.dts @@ -0,0 +1,36 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L CDP"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <1 0>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; + +&tasha_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm636-cdp.dtsi new file mode 100644 index 000000000000..89d42209b34c --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-cdp.dtsi @@ -0,0 +1,16 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "sdm660-cdp.dtsi" +/ { +}; + diff --git a/arch/arm/boot/dts/qcom/sdm636-headset-jacktype-no-cdp.dts b/arch/arm/boot/dts/qcom/sdm636-headset-jacktype-no-cdp.dts new file mode 100644 index 000000000000..4d47ac1ef517 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-headset-jacktype-no-cdp.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L, Headset Jacktype NO, CDP"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <1 2>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-headset-jacktype-no-rcm.dts b/arch/arm/boot/dts/qcom/sdm636-headset-jacktype-no-rcm.dts new file mode 100644 index 000000000000..dfe066d1902c --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-headset-jacktype-no-rcm.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L, Headset Jacktype NO, RCM"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <21 2>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-internal-codec-cdp.dts b/arch/arm/boot/dts/qcom/sdm636-internal-codec-cdp.dts new file mode 100644 index 000000000000..725e9739a487 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-cdp.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L Int. Audio Codec CDP"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <1 1>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-internal-codec-mtp.dts b/arch/arm/boot/dts/qcom/sdm636-internal-codec-mtp.dts new file mode 100644 index 000000000000..3630e329b13d --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-mtp.dts @@ -0,0 +1,30 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-mtp.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L Int. Audio Codec MTP"; + compatible = "qcom,sdm636-mtp", "qcom,sdm636", "qcom,mtp"; + qcom,board-id = <8 1>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&int_codec { + qcom,model = "sdm660-snd-card-mtp"; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-cdp.dts new file mode 100644 index 000000000000..416cd3754a10 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-cdp.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660A Int. Audio Codec CDP"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <1 1>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-mtp.dts new file mode 100644 index 000000000000..e9ad9048c6e9 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-mtp.dts @@ -0,0 +1,31 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-mtp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660A Int. Audio Codec MTP"; + compatible = "qcom,sdm636-mtp", "qcom,sdm636", "qcom,mtp"; + qcom,board-id = <8 1>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&int_codec { + qcom,model = "sdm660-snd-card-mtp"; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-rcm.dts b/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-rcm.dts new file mode 100644 index 000000000000..1812423ed948 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-rcm.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660A Int. Audio Codec RCM"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <21 1>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-internal-codec-rcm.dts b/arch/arm/boot/dts/qcom/sdm636-internal-codec-rcm.dts new file mode 100644 index 000000000000..4bb67fa5bb71 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-rcm.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L Int. Audio Codec RCM"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <21 1>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-mtp.dts b/arch/arm/boot/dts/qcom/sdm636-mtp.dts new file mode 100644 index 000000000000..7f08e44148b5 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-mtp.dts @@ -0,0 +1,30 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-mtp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L MTP"; + compatible = "qcom,sdm636-mtp", "qcom,sdm636", "qcom,mtp"; + qcom,board-id = <8 0>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm636-mtp.dtsi new file mode 100644 index 000000000000..4364db2bc724 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-mtp.dtsi @@ -0,0 +1,16 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "sdm660-mtp.dtsi" +/ { +}; + diff --git a/arch/arm/boot/dts/qcom/sdm636-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm636-pm660a-cdp.dts new file mode 100644 index 000000000000..84a28770d5a8 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-cdp.dts @@ -0,0 +1,54 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660A CDP"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <1 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; + +&mdss_dsi1 { + status = "disabled"; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; + +&tavil_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; + +&tasha_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-pm660a-headset-jacktype-no-cdp.dts b/arch/arm/boot/dts/qcom/sdm636-pm660a-headset-jacktype-no-cdp.dts new file mode 100644 index 000000000000..b2517e32ae26 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-headset-jacktype-no-cdp.dts @@ -0,0 +1,38 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660A, Headset Jacktype NO, CDP"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <1 2>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&mdss_dsi0 { + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; + +&mdss_dsi1 { + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-pm660a-headset-jacktype-no-rcm.dts b/arch/arm/boot/dts/qcom/sdm636-pm660a-headset-jacktype-no-rcm.dts new file mode 100644 index 000000000000..20b4365380bb --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-headset-jacktype-no-rcm.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660A, Headset Jacktype NO, RCM"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <21 2>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm636-pm660a-mtp.dts new file mode 100644 index 000000000000..966a00af7216 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-mtp.dts @@ -0,0 +1,48 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-mtp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660A MTP"; + compatible = "qcom,sdm636-mtp", "qcom,sdm636", "qcom,mtp"; + qcom,board-id = <8 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; + +&mdss_dsi1 { + status = "disabled"; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; + +&tavil_snd { + qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-pm660a-qrd.dts b/arch/arm/boot/dts/qcom/sdm636-pm660a-qrd.dts new file mode 100644 index 000000000000..d8b6c744a204 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-qrd.dts @@ -0,0 +1,56 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-qrd.dtsi" +#include "msm-pm660a.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660A QRD"; + compatible = "qcom,sdm636-qrd", "qcom,sdm636", "qcom,qrd"; + qcom,board-id = <0x0012000b 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&pm660a_oledb { + status = "okay"; + qcom,oledb-default-voltage-mv = <6400>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-pm660a-rcm.dts b/arch/arm/boot/dts/qcom/sdm636-pm660a-rcm.dts new file mode 100644 index 000000000000..a23b074b4392 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-rcm.dts @@ -0,0 +1,36 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660A RCM"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <21 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; + +&tasha_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-qrd.dts b/arch/arm/boot/dts/qcom/sdm636-qrd.dts new file mode 100644 index 000000000000..a2d22f281a5d --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-qrd.dts @@ -0,0 +1,87 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-qrd.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L QRD"; + compatible = "qcom,sdm636-qrd", "qcom,sdm636", "qcom,qrd"; + qcom,board-id = <0x1000b 0>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_fb0 { + qcom,mdss-mixer-swap; +}; + +&mdss_dsi { + hw-config = "split_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_dual_nt36850_truly_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; + +&mdss_dsi1 { + qcom,dsi-pref-prim-pan = <&dsi_dual_nt36850_truly_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; + +&dsi_dual_nt36850_truly_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; + +&pm660l_wled { + qcom,led-strings-list = [00 01]; +}; + +&soc { + hbtp { + compatible = "qcom,hbtp-input"; + pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; + pinctrl-0 = <&ts_rst_active>; + pinctrl-1 = <&ts_rst_suspend>; + vcc_ana-supply = <&pm660l_l3>; + vcc_dig-supply = <&pm660_l13>; + qcom,afe-load = <20000>; + qcom,afe-vtg-min = <3008000>; + qcom,afe-vtg-max = <3008000>; + qcom,dig-load = <40000>; + qcom,dig-vtg-min = <1808000>; + qcom,dig-vtg-max = <1808000>; + qcom,fb-resume-delay-us = <10000>; + qcom,afe-force-power-on; + qcom,afe-power-on-delay-us = <1000>; + qcom,afe-power-off-delay-us = <6>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm636-qrd.dtsi new file mode 100644 index 000000000000..8791e7420148 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-qrd.dtsi @@ -0,0 +1,16 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "sdm660-qrd.dtsi" +/ { +}; + diff --git a/arch/arm/boot/dts/qcom/sdm636-rcm.dts b/arch/arm/boot/dts/qcom/sdm636-rcm.dts new file mode 100644 index 000000000000..5566610566f8 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-rcm.dts @@ -0,0 +1,36 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L RCM"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <21 0>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; + +&tasha_snd { + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-usbc-audio-mtp.dts b/arch/arm/boot/dts/qcom/sdm636-usbc-audio-mtp.dts new file mode 100644 index 000000000000..90879cdf35cc --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-usbc-audio-mtp.dts @@ -0,0 +1,31 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-mtp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L, USBC Audio MTP"; + compatible = "qcom,sdm636-mtp", "qcom,sdm636", "qcom,mtp"; + qcom,board-id = <8 2>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>; + qcom,msm-mbhc-usbc-audio-supported = <1>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636-usbc-audio-rcm.dts b/arch/arm/boot/dts/qcom/sdm636-usbc-audio-rcm.dts new file mode 100644 index 000000000000..6fff927913fa --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-usbc-audio-rcm.dts @@ -0,0 +1,30 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "sdm636.dtsi" +#include "sdm636-cdp.dtsi" +#include "sdm660-external-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636 PM660 + PM660L, USBC Audio, RCM"; + compatible = "qcom,sdm636-cdp", "qcom,sdm636", "qcom,cdp"; + qcom,board-id = <21 3>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&tavil_snd { + qcom,msm-mbhc-usbc-audio-supported = <1>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm636.dtsi b/arch/arm/boot/dts/qcom/sdm636.dtsi new file mode 100644 index 000000000000..8024eee279cb --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636.dtsi @@ -0,0 +1,73 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "sdm660.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 636"; + compatible = "qcom,sdm636"; + qcom,msm-id = <345 0x0>; + + reserved-memory { + /delete-node/ cdsp_fw_region@94a00000; + + buffer_mem: buffer_region@94a00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x94a00000 0x0 0x100000>; + }; + }; +}; + +&soc { + /delete-node/ qcom,turing@1a300000; + + /delete-node/ cti@7068000; + /delete-node/ turing_etm0; + funnel@6042000 { + ports { + /delete-node/ port@4; + }; + }; + + devfreq_memlat_4: qcom,arm-memlat-mon-4 { + qcom,core-dev-table = + < 1113600 762 >, + < 1401600 3879 >, + < 1804800 5163 >; + }; + + devfreq_cpufreq: devfreq-cpufreq { + mincpubw-cpufreq { + cpu-to-dev-map-4 = + < 1113600 762 >, + < 1401600 2086 >, + < 1747200 2929 >, + < 1804800 5163 >; + }; + }; +}; + +&soc { + /delete-node/ arm,smmu-turing_q6@5180000; + qcom,msm_fastrpc { + /delete-node/ qcom,msm_fastrpc_compute_cb5; + /delete-node/ qcom,msm_fastrpc_compute_cb6; + /delete-node/ qcom,msm_fastrpc_compute_cb7; + /delete-node/ qcom,msm_fastrpc_compute_cb8; + /delete-node/ qcom,msm_fastrpc_compute_cb9; + /delete-node/ qcom,msm_fastrpc_compute_cb10; + /delete-node/ qcom,msm_fastrpc_compute_cb11; + /delete-node/ qcom,msm_fastrpc_compute_cb12; + /delete-node/ qcom,msm_fastrpc_compute_cb13; + }; +}; diff --git a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi index 46f77e9a3253..476fedec35a4 100644 --- a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi @@ -29,6 +29,16 @@ qcom,switch-source = <&pm660l_switch1>; status = "ok"; }; + + cam_actuator_regulator: cam_actuator_fixed_regulator { + compatible = "regulator-fixed"; + regulator-name = "cam_actuator_regulator"; + regulator-min-microvolt = <3600000>; + regulator-max-microvolt = <3600000>; + enable-active-high; + gpio = <&tlmm 50 0>; + vin-supply = <&pm660l_bob>; + }; }; &cci { @@ -37,14 +47,11 @@ reg = <0x0>; compatible = "qcom,actuator"; qcom,cci-master = <0>; - gpios = <&tlmm 50 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + qcom,cam-vreg-op-mode = <0>; }; actuator1: qcom,actuator@1 { @@ -52,14 +59,11 @@ reg = <0x1>; compatible = "qcom,actuator"; qcom,cci-master = <1>; - gpios = <&tlmm 50 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + qcom,cam-vreg-op-mode = <0>; }; actuator2: qcom,actuator@2 { @@ -67,14 +71,11 @@ reg = <0x2>; compatible = "qcom,actuator"; qcom,cci-master = <1>; - gpios = <&tlmm 50 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + qcom,cam-vreg-op-mode = <0>; }; ois0: qcom,ois@0 { @@ -82,15 +83,31 @@ reg = <0x0>; compatible = "qcom,ois"; qcom,cci-master = <0>; - gpios = <&tlmm 50 0>; - qcom,gpio-vaf = <0>; + cam_vaf-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + qcom,cam-vreg-op-mode = <0>; + status = "disabled"; + }; + + tof0: qcom,tof@29{ + cell-index = <0>; + reg = <0x29>; + compatible = "st,stmvl53l0"; + qcom,cci-master = <0>; + cam_laser-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_laser"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_tof_active>; + pinctrl-1 = <&cam_tof_suspend>; + stm,irq-gpio = <&tlmm 45 0x2008>; + gpios = <&tlmm 42 0>; qcom,gpio-req-tbl-num = <0>; qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; - status = "disabled"; + qcom,gpio-req-tbl-label = "RNG_EN"; }; eeprom0: qcom,eeprom@0 { diff --git a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi index 94166bf8dd3e..11eb288804ff 100644 --- a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi @@ -40,6 +40,16 @@ vin-supply = <&pm660l_bob>; }; + cam_actuator_regulator: cam_actuator_fixed_regulator { + compatible = "regulator-fixed"; + regulator-name = "cam_actuator_regulator"; + regulator-min-microvolt = <3600000>; + regulator-max-microvolt = <3600000>; + enable-active-high; + gpio = <&tlmm 50 0>; + vin-supply = <&pm660l_bob>; + }; + cam_dvdd_gpio_regulator: cam_dvdd_fixed_regulator { compatible = "regulator-fixed"; regulator-name = "cam_dvdd_gpio_regulator"; @@ -67,14 +77,11 @@ reg = <0x0>; compatible = "qcom,actuator"; qcom,cci-master = <0>; - gpios = <&tlmm 50 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + qcom,cam-vreg-op-mode = <0>; }; actuator1: qcom,actuator@1 { @@ -82,14 +89,11 @@ reg = <0x1>; compatible = "qcom,actuator"; qcom,cci-master = <1>; - gpios = <&tlmm 50 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + qcom,cam-vreg-op-mode = <0>; }; actuator2: qcom,actuator@2 { @@ -97,14 +101,11 @@ reg = <0x2>; compatible = "qcom,actuator"; qcom,cci-master = <1>; - gpios = <&tlmm 50 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + qcom,cam-vreg-op-mode = <0>; }; ois0: qcom,ois@0 { @@ -112,15 +113,31 @@ reg = <0x0>; compatible = "qcom,ois"; qcom,cci-master = <0>; - gpios = <&tlmm 50 0>; - qcom,gpio-vaf = <0>; + cam_vaf-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + qcom,cam-vreg-op-mode = <0>; + status = "disabled"; + }; + + tof0: qcom,tof@29{ + cell-index = <0>; + reg = <0x29>; + compatible = "st,stmvl53l0"; + qcom,cci-master = <0>; + cam_laser-supply = <&cam_actuator_regulator>; + qcom,cam-vreg-name = "cam_laser"; + qcom,cam-vreg-min-voltage = <3600000>; + qcom,cam-vreg-max-voltage = <3600000>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_tof_active>; + pinctrl-1 = <&cam_tof_suspend>; + stm,irq-gpio = <&tlmm 45 0x2008>; + gpios = <&tlmm 42 0>; qcom,gpio-req-tbl-num = <0>; qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; - status = "disabled"; + qcom,gpio-req-tbl-label = "RNG_EN"; }; eeprom0: qcom,eeprom@0 { diff --git a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi index 45b7201fbf71..50f5d83346c6 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi @@ -56,6 +56,16 @@ qcom,master-en = <1>; status = "okay"; }; + + /* GPIO 11 for Home Key */ + gpio@ca00 { + status = "okay"; + qcom,mode = <0>; + qcom,pull = <0>; + qcom,vin-sel = <0>; + qcom,src-sel = <0>; + qcom,out-strength = <1>; + }; }; &i2c_6 { /* BLSP1 QUP6 (NFC) */ diff --git a/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi b/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi index d902078b1048..e55a67e68b36 100644 --- a/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi @@ -1101,6 +1101,34 @@ }; }; + cam_tof_active: cam_tof_active { + /* LASER */ + mux { + pins = "gpio50", "gpio42", "gpio45"; + function = "gpio"; + }; + + config { + pins = "gpio50", "gpio42", "gpio45"; + bias-pull-up; + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_tof_suspend: cam_tof_suspend { + /* LASER */ + mux { + pins = "gpio50", "gpio42", "gpio45"; + function = "gpio"; + }; + + config { + pins = "gpio50", "gpio42", "gpio45"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + cam_sensor_mclk0_active: cam_sensor_mclk0_active { /* MCLK0 */ mux { diff --git a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi index a4111f6d1b94..6556c986ae75 100644 --- a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi @@ -700,11 +700,33 @@ regulator-max-microvolt = <8>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <16>; - qcom,cpr-speed-bins = <2>; - qcom,cpr-speed-bin-corners = <8 8>; - qcom,cpr-corners = <8>; - qcom,cpr-corner-fmax-map = <2 3 4 5 8>; + qcom,cpr-fuse-combos = <32>; + qcom,cpr-speed-bins = <4>; + qcom,cpr-speed-bin-corners = <8 8 0 8>; + qcom,cpr-corners = + /* Speed bin 0 */ + <8 8 8 8 8 8 8 8>, + + /* Speed bin 1 */ + <8 8 8 8 8 8 8 8>, + + /* Speed bin 2 */ + <0 0 0 0 0 0 0 0>, + + /* Speed bin 3 */ + <8 8 8 8 8 8 8 8>; + qcom,cpr-corner-fmax-map = + /* Speed bin 0 */ + <2 3 4 5 8>, + + /* Speed bin 1 */ + <2 3 4 5 8>, + + /* Speed bin 2 */ + <0 0 0 0 0>, + + /* Speed bin 3 */ + <2 3 4 5 8>; qcom,cpr-voltage-ceiling = < 724000 724000 724000 788000 868000 @@ -715,9 +737,20 @@ 744000 784000 844000>; qcom,corner-frequencies = + /* Speed bin 0 */ + <300000000 633600000 902400000 + 1113600000 1401600000 1536000000 + 1747200000 1843200000>, + + /* Speed bin 1 */ + <300000000 633600000 902400000 + 1113600000 1401600000 1536000000 + 1747200000 1843200000>, + + /* Speed bin 3 */ <300000000 633600000 902400000 1113600000 1401600000 1536000000 - 1747200000 1843200000>; + 1612800000 1843200000>; qcom,allow-voltage-interpolation; qcom,allow-quotient-interpolation; @@ -806,11 +839,34 @@ regulator-max-microvolt = <7>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <16>; - qcom,cpr-speed-bins = <2>; - qcom,cpr-speed-bin-corners = <7 7>; - qcom,cpr-corners = <7>; - qcom,cpr-corner-fmax-map = <2 3 4 6 7>; + qcom,cpr-fuse-combos = <32>; + qcom,cpr-speed-bins = <4>; + qcom,cpr-speed-bin-corners = <7 7 0 7>; + qcom,cpr-corners = + /* Speed-bin 0 */ + <7 7 7 7 7 7 7 7>, + + /* Speed-bin 1 */ + <7 7 7 7 7 7 7 7>, + + /* Speed-bin 1 */ + <0 0 0 0 0 0 0 0>, + + /* Speed-bin 3 */ + <7 7 7 7 7 7 7 7>; + + qcom,cpr-corner-fmax-map = + /* Speed-bin 0 */ + <2 3 4 6 7>, + + /* Speed-bin 1 */ + <2 3 4 6 7>, + + /* Speed-bin 2 */ + <0 0 0 0 0>, + + /* Speed-bin 3 */ + <2 3 4 6 7>; qcom,cpr-voltage-ceiling = <724000 724000 788000 868000 @@ -829,6 +885,11 @@ /* Speed bin 1 */ <300000000 1113600000 1401600000 1747200000 1958400000 2150400000 + 2208000000>, + + /* Speed bin 3 */ + <300000000 1113600000 1401600000 + 1747200000 1804800000 2150400000 2208000000>; qcom,allow-voltage-interpolation; diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi index f14c9a32c2f9..c436ce643091 100644 --- a/arch/arm/boot/dts/qcom/sdm660.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660.dtsi @@ -672,6 +672,16 @@ clock-names = "core", "iface"; }; + qcom,qbt1000 { + compatible = "qcom,qbt1000"; + clock-names = "core", "iface"; + clocks = <&clock_gcc GCC_BLSP1_QUP3_SPI_APPS_CLK>, + <&clock_gcc GCC_BLSP1_AHB_CLK>; + clock-frequency = <15000000>; + qcom,ipc-gpio = <&tlmm 72 0>; + qcom,finger-detect-gpio = <&pm660_gpios 11 0>; + }; + qcom,sensor-information { compatible = "qcom,sensor-information"; sensor_information0: qcom,sensor-information-0 { @@ -1244,10 +1254,11 @@ compatible = "qcom,clk-cpu-osm"; reg = <0x179c0000 0x4000>, <0x17916000 0x1000>, <0x17816000 0x1000>, <0x179d1000 0x1000>, - <0x00784130 0x8>, <0x17914800 0x800>; + <0x00784130 0x8>, <0x00784130 0x8>, + <0x17914800 0x800>; reg-names = "osm", "pwrcl_pll", "perfcl_pll", - "apcs_common", "perfcl_efuse", - "pwrcl_acd"; + "apcs_common", "pwrcl_efuse", + "perfcl_efuse", "pwrcl_acd"; qcom,acdtd-val = <0x0000a111 0x0000a111>; qcom,acdcr-val = <0x002c5ffd 0x002c5ffd>; @@ -1273,6 +1284,25 @@ < 1747200000 0x0404005b 0x09480048 0x2 7 >, < 1843200000 0x04040060 0x094c004c 0x3 8 >; + qcom,pwrcl-speedbin1-v0 = + < 300000000 0x0004000f 0x01200020 0x1 1 >, + < 633600000 0x05040021 0x03200020 0x1 2 >, + < 902400000 0x0404002f 0x04260026 0x1 3 >, + < 1113600000 0x0404003a 0x052e002e 0x2 4 >, + < 1401600000 0x04040049 0x073a003a 0x2 5 >, + < 1536000000 0x04040050 0x08400040 0x2 6 >, + < 1747200000 0x0404005b 0x09480048 0x2 7 >, + < 1843200000 0x04040060 0x094c004c 0x3 8 >; + + qcom,pwrcl-speedbin3-v0 = + < 300000000 0x0004000f 0x01200020 0x1 1 >, + < 633600000 0x05040021 0x03200020 0x1 2 >, + < 902400000 0x0404002f 0x04260026 0x1 3 >, + < 1113600000 0x0404003a 0x052e002e 0x2 4 >, + < 1401600000 0x04040049 0x073a003a 0x2 5 >, + < 1536000000 0x04040050 0x08400040 0x2 6 >, + < 1612800000 0x04040054 0x09430043 0x2 7 >; + qcom,perfcl-speedbin0-v0 = < 300000000 0x0004000f 0x01200020 0x1 1 >, < 1113600000 0x0404003a 0x052e002e 0x1 2 >, @@ -1291,6 +1321,13 @@ < 2150400000 0x04040070 0x0b590059 0x2 6 >, < 2208000000 0x04040073 0x0b5c005c 0x3 7 >; + qcom,perfcl-speedbin3-v0 = + < 300000000 0x0004000f 0x01200020 0x1 1 >, + < 1113600000 0x0404003a 0x052e002e 0x1 2 >, + < 1401600000 0x04040049 0x073a003a 0x2 3 >, + < 1747200000 0x0404005b 0x09480048 0x2 4 >, + < 1804800000 0x0404005e 0x094b004b 0x2 5 >; + qcom,up-timer = <1000 1000>; qcom,down-timer = <1000 1000>; qcom,pc-override-index = <0 0>; @@ -1344,6 +1381,7 @@ < 1113600 >, < 1401600 >, < 1536000 >, + < 1612800 >, < 1747200 >, < 1843200 >; @@ -1351,6 +1389,7 @@ < 1113600 >, < 1401600 >, < 1747200 >, + < 1804800 >, < 1958400 >, < 2150400 >, < 2208000 >, @@ -2045,6 +2084,7 @@ qcom,firmware-name = "modem"; qcom,pil-self-auth; qcom,sysmon-id = <0>; + qcom,minidump-id = <0>; qcom,ssctl-instance-id = <0x12>; qcom,qdsp6v62-1-5; memory-region = <&modem_fw_mem>; @@ -2119,6 +2159,11 @@ compatible = "qcom,msm-imem-diag-dload"; reg = <0xc8 200>; }; + + ss_mdump@b88 { + compatible = "qcom,msm-imem-minidump"; + reg = <0xb88 28>; + }; }; qcom,ghd { diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts index 7cf55acf900b..5c0bbbccafea 100644 --- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts @@ -42,6 +42,13 @@ reg = <0x1 0x0 0 0x10000000>; label = "ion_system_mem"; }; + qseecom_mem: qseecom_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; + reusable; + alignment = <0 0x400000>; + size = <0 0x1400000>; + }; ion_audio: ion_audio_region { reg = <0 0xc8000000 0 0x00400000>; label = "ion_audio_mem"; @@ -78,6 +85,10 @@ <&afe_proxy_rx>, <&afe_proxy_tx>, <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, <&incall_music2_rx>, + <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>, + <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>, + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>, + <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>, <&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>, <&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>, <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, @@ -96,6 +107,10 @@ "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770", + "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867", + "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871", + "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866", + "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870", "msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883", "msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887", "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", @@ -285,6 +300,82 @@ }; }; + qcom,msm-dai-tdm-pri-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37120>; + qcom,msm-cpudai-tdm-group-num-ports = <4>; + qcom,msm-cpudai-tdm-group-port-id = <36864 36866 36868 36870>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36864>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36866>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36868>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36870>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-pri-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37121>; + qcom,msm-cpudai-tdm-group-num-ports = <4>; + qcom,msm-cpudai-tdm-group-port-id = <36865 36867 36869 36871>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36865>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36867>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36869>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36871>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + qcom,msm-dai-tdm-sec-tx { compatible = "qcom,msm-dai-tdm"; qcom,msm-cpudai-tdm-group-id = <37137>; @@ -477,6 +568,22 @@ }; }; + qcom_seecom: qseecom@86600000 { + compatible = "qcom,qseecom"; + reg = <0x86600000 0x2200000>; + reg-names = "secapp-region"; + qcom,hlos-num-ce-hw-instances = <1>; + qcom,hlos-ce-hw-instance = <0>; + qcom,qsee-ce-hw-instance = <0>; + qcom,disk-encrypt-pipe-pair = <2>; + qcom,no-clock-support; + qcom,msm-bus,name = "qseecom-noc"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,ce-opp-freq = <171430000>; + qcom,qsee-reentrancy-support = <2>; + }; + hostless: qcom,msm-pcm-hostless { compatible = "qcom,msm-pcm-hostless"; }; diff --git a/arch/arm/configs/s3c2410_defconfig b/arch/arm/configs/s3c2410_defconfig index f3142369f594..01116ee1284b 100644 --- a/arch/arm/configs/s3c2410_defconfig +++ b/arch/arm/configs/s3c2410_defconfig @@ -87,9 +87,9 @@ CONFIG_IPV6_TUNNEL=m CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=m -CONFIG_NF_CT_PROTO_SCTP=m -CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig index 43b6432118f0..878e720a927b 100644 --- a/arch/arm/configs/sdm660-perf_defconfig +++ b/arch/arm/configs/sdm660-perf_defconfig @@ -656,6 +656,7 @@ CONFIG_CORESIGHT_QPDI=y CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y CONFIG_SECURITY=y +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SMACK=y CONFIG_CRYPTO_ECHAINIV=y diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig index e3aa35da81ce..524abcf83e77 100644 --- a/arch/arm/configs/sdm660_defconfig +++ b/arch/arm/configs/sdm660_defconfig @@ -694,6 +694,7 @@ CONFIG_CORESIGHT_QPDI=y CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y CONFIG_SECURITY=y +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SMACK=y CONFIG_CRYPTO_ECHAINIV=y diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index d2315ffd8f12..f13ae153fb24 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h @@ -112,12 +112,8 @@ int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs); #define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE 4096 -/* This is the location that an ET_DYN program is loaded if exec'ed. Typical - use of this is to invoke "./ld.so someprog" to test out a new version of - the loader. We need to make sure that it is out of the way of the program - that it will "exec", and that there is sufficient room for the brk. */ - -#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) +/* This is the base location for PIE (ET_DYN with INTERP) loads. */ +#define ELF_ET_DYN_BASE 0x400000UL /* When the program starts, a1 contains a pointer to a function to be registered with atexit, as per the SVR4 ABI. A value of 0 means we diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts index ce5d848251fa..7b34822d61e9 100644 --- a/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts +++ b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts @@ -26,7 +26,7 @@ stdout-path = "serial0:115200n8"; }; - memory { + memory@0 { device_type = "memory"; reg = <0x0 0x0 0x40000000>; }; diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi index 857eda5c7217..172402cc1a0f 100644 --- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi +++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi @@ -71,7 +71,7 @@ <1 10 0xf01>; }; - amba_apu { + amba_apu: amba_apu@0 { compatible = "simple-bus"; #address-cells = <2>; #size-cells = <1>; @@ -191,7 +191,7 @@ }; i2c0: i2c@ff020000 { - compatible = "cdns,i2c-r1p10"; + compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10"; status = "disabled"; interrupt-parent = <&gic>; interrupts = <0 17 4>; @@ -202,7 +202,7 @@ }; i2c1: i2c@ff030000 { - compatible = "cdns,i2c-r1p10"; + compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10"; status = "disabled"; interrupt-parent = <&gic>; interrupts = <0 18 4>; diff --git a/arch/arm64/configs/msm-auto-perf_defconfig b/arch/arm64/configs/msm-auto-perf_defconfig index efdfb4da2de2..28d10091da43 100644 --- a/arch/arm64/configs/msm-auto-perf_defconfig +++ b/arch/arm64/configs/msm-auto-perf_defconfig @@ -261,6 +261,7 @@ CONFIG_NETDEVICES=y CONFIG_BONDING=y CONFIG_DUMMY=y CONFIG_TUN=y +CONFIG_ATL1C=y CONFIG_E1000E=y CONFIG_MSM_RMNET_MHI=y CONFIG_RNDIS_IPA=y @@ -299,8 +300,6 @@ CONFIG_INPUT_GPIO=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set -# CONFIG_DEVMEM is not set -# CONFIG_DEVKMEM is not set CONFIG_SERIAL_MSM_HS=y CONFIG_SERIAL_MSM_SMD=y CONFIG_DIAG_CHAR=y @@ -319,6 +318,7 @@ CONFIG_SPI=y CONFIG_SPI_QUP=y CONFIG_SPI_SPIDEV=y CONFIG_SPMI=y +CONFIG_PPS_CLIENT_GPIO=y CONFIG_PINCTRL_MSM8996=y CONFIG_GPIOLIB=y CONFIG_GPIO_SYSFS=y diff --git a/arch/arm64/configs/msm-auto_defconfig b/arch/arm64/configs/msm-auto_defconfig index e9ef95772ebd..1783707399ca 100644 --- a/arch/arm64/configs/msm-auto_defconfig +++ b/arch/arm64/configs/msm-auto_defconfig @@ -262,6 +262,7 @@ CONFIG_NETDEVICES=y CONFIG_BONDING=y CONFIG_DUMMY=y CONFIG_TUN=y +CONFIG_ATL1C=y CONFIG_E1000E=y CONFIG_MSM_RMNET_MHI=y CONFIG_RNDIS_IPA=y @@ -300,8 +301,6 @@ CONFIG_INPUT_GPIO=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set -# CONFIG_DEVMEM is not set -# CONFIG_DEVKMEM is not set CONFIG_SERIAL_MSM=y CONFIG_SERIAL_MSM_CONSOLE=y CONFIG_SERIAL_MSM_HS=y @@ -322,6 +321,7 @@ CONFIG_SPI=y CONFIG_SPI_QUP=y CONFIG_SPI_SPIDEV=y CONFIG_SPMI=y +CONFIG_PPS_CLIENT_GPIO=y CONFIG_PINCTRL_MSM8996=y CONFIG_GPIOLIB=y CONFIG_GPIO_SYSFS=y diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index acde18d2fe31..61418724b897 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -34,7 +34,7 @@ CONFIG_EMBEDDED=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y -CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index f510f43427ce..ee2b9fa628ff 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -31,7 +31,7 @@ CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y -CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 894cb466b075..e16fc58ce913 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -40,7 +40,7 @@ CONFIG_EMBEDDED=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y -CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y @@ -295,6 +295,7 @@ CONFIG_WCNSS_MEM_PRE_ALLOC=y CONFIG_ATH_CARDS=y CONFIG_WIL6210=m CONFIG_CLD_LL_CORE=y +CONFIG_CNSS_GENL=y CONFIG_INPUT_EVDEV=y CONFIG_INPUT_KEYRESET=y CONFIG_KEYBOARD_GPIO=y @@ -320,6 +321,7 @@ CONFIG_SERIAL_MSM_SMD=y CONFIG_DIAG_CHAR=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y +# CONFIG_DEVPORT is not set CONFIG_MSM_ADSPRPC=y CONFIG_MSM_RDBG=m CONFIG_I2C_CHARDEV=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 993e350ef000..77157bb85ee1 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -38,7 +38,7 @@ CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y -CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y @@ -296,6 +296,7 @@ CONFIG_WCNSS_MEM_PRE_ALLOC=y CONFIG_ATH_CARDS=y CONFIG_WIL6210=m CONFIG_CLD_LL_CORE=y +CONFIG_CNSS_GENL=y CONFIG_INPUT_EVDEV=y CONFIG_INPUT_KEYRESET=y CONFIG_KEYBOARD_GPIO=y @@ -324,6 +325,7 @@ CONFIG_SERIAL_MSM_SMD=y CONFIG_DIAG_CHAR=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y +# CONFIG_DEVPORT is not set CONFIG_MSM_ADSPRPC=y CONFIG_MSM_RDBG=m CONFIG_I2C_CHARDEV=y diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig index b6cb1a4b8574..c92551c69bf8 100644 --- a/arch/arm64/configs/sdm660-perf_defconfig +++ b/arch/arm64/configs/sdm660-perf_defconfig @@ -38,7 +38,7 @@ CONFIG_EMBEDDED=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y -CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y @@ -69,6 +69,7 @@ CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y CONFIG_CP15_BARRIER_EMULATION=y CONFIG_SETEND_EMULATION=y +CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_RANDOMIZE_BASE=y # CONFIG_EFI is not set CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y @@ -299,6 +300,7 @@ CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_QPNP_POWER_ON=y CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_STMVL53L0=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set @@ -574,6 +576,7 @@ CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_AVTIMER=y CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_QBT1000=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y @@ -640,6 +643,7 @@ CONFIG_CORESIGHT_QPDI=y CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y CONFIG_SECURITY=y +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SMACK=y CONFIG_CRYPTO_ECHAINIV=y diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig index ccef87ff6a04..6b45087e2603 100644 --- a/arch/arm64/configs/sdm660_defconfig +++ b/arch/arm64/configs/sdm660_defconfig @@ -40,7 +40,7 @@ CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y -CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y @@ -73,6 +73,7 @@ CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y CONFIG_CP15_BARRIER_EMULATION=y CONFIG_SETEND_EMULATION=y +CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_RANDOMIZE_BASE=y CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set @@ -308,6 +309,7 @@ CONFIG_INPUT_QPNP_POWER_ON=y CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y +CONFIG_INPUT_STMVL53L0=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set @@ -599,6 +601,7 @@ CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_AVTIMER=y CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_QBT1000=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y @@ -713,6 +716,7 @@ CONFIG_CORESIGHT_QPDI=y CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y CONFIG_SECURITY=y +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SMACK=y CONFIG_CRYPTO_ECHAINIV=y diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index a383c288ef49..b98332269462 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -114,12 +114,11 @@ #define ELF_EXEC_PAGESIZE PAGE_SIZE /* - * This is the location that an ET_DYN program is loaded if exec'ed. Typical - * use of this is to invoke "./ld.so someprog" to test out a new version of - * the loader. We need to make sure that it is out of the way of the program - * that it will "exec", and that there is sufficient room for the brk. + * This is the base location for PIE (ET_DYN with INTERP) loads. On + * 64-bit, this is raised to 4GB to leave the entire 32-bit address + * space open for things that want to use the area for 32-bit pointers. */ -#define ELF_ET_DYN_BASE (2 * TASK_SIZE_64 / 3) +#define ELF_ET_DYN_BASE 0x100000000UL #ifndef __ASSEMBLY__ @@ -170,7 +169,8 @@ extern int arch_setup_additional_pages(struct linux_binprm *bprm, #ifdef CONFIG_COMPAT -#define COMPAT_ELF_ET_DYN_BASE (2 * TASK_SIZE_32 / 3) +/* PIE load location for compat arm. Must match ARM ELF_ET_DYN_BASE. */ +#define COMPAT_ELF_ET_DYN_BASE 0x000400000UL /* AArch32 registers. */ #define COMPAT_ELF_NGREG 18 diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index eea245bf546a..3112c2a9d96f 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -167,9 +167,9 @@ static inline u64 __raw_readq_no_log(const volatile void __iomem *addr) #define readq_relaxed_no_log(c) ({ u64 __v = le64_to_cpu((__force __le64)__raw_readq_no_log(c)); __v; }) #define writeb_relaxed_no_log(v, c) ((void)__raw_writeb_no_log((v), (c))) -#define writew_relaxed_no_log(v, c) ((void)__raw_writew_no_log((__force u16)cpu_to_le32(v), (c))) +#define writew_relaxed_no_log(v, c) ((void)__raw_writew_no_log((__force u16)cpu_to_le16(v), (c))) #define writel_relaxed_no_log(v, c) ((void)__raw_writel_no_log((__force u32)cpu_to_le32(v), (c))) -#define writeq_relaxed_no_log(v, c) ((void)__raw_writeq_no_log((__force u64)cpu_to_le32(v), (c))) +#define writeq_relaxed_no_log(v, c) ((void)__raw_writeq_no_log((__force u64)cpu_to_le64(v), (c))) /* * I/O memory access primitives. Reads are ordered relative to any diff --git a/arch/arm64/kernel/perf_trace_counters.c b/arch/arm64/kernel/perf_trace_counters.c index 748ad449fc18..dc92b29ac103 100644 --- a/arch/arm64/kernel/perf_trace_counters.c +++ b/arch/arm64/kernel/perf_trace_counters.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -124,6 +124,7 @@ static ssize_t write_enabled_perftp_file_bool(struct file *file, char buf[32]; size_t buf_size; + buf[0] = 0; buf_size = min(count, (sizeof(buf)-1)); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 1804aea44faa..2720d47da366 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -92,21 +92,21 @@ void show_pte(struct mm_struct *mm, unsigned long addr) break; pud = pud_offset(pgd, addr); - printk(", *pud=%016llx", pud_val(*pud)); + pr_cont(", *pud=%016llx", pud_val(*pud)); if (pud_none(*pud) || pud_bad(*pud)) break; pmd = pmd_offset(pud, addr); - printk(", *pmd=%016llx", pmd_val(*pmd)); + pr_cont(", *pmd=%016llx", pmd_val(*pmd)); if (pmd_none(*pmd) || pmd_bad(*pmd)) break; pte = pte_offset_map(pmd, addr); - printk(", *pte=%016llx", pte_val(*pte)); + pr_cont(", *pte=%016llx", pte_val(*pte)); pte_unmap(pte); } while(0); - printk("\n"); + pr_cont("\n"); } #ifdef CONFIG_ARM64_HW_AFDBM diff --git a/arch/mips/include/asm/branch.h b/arch/mips/include/asm/branch.h index de781cf54bc7..da80878f2c0d 100644 --- a/arch/mips/include/asm/branch.h +++ b/arch/mips/include/asm/branch.h @@ -74,10 +74,7 @@ static inline int compute_return_epc(struct pt_regs *regs) return __microMIPS_compute_return_epc(regs); if (cpu_has_mips16) return __MIPS16e_compute_return_epc(regs); - return regs->cp0_epc; - } - - if (!delay_slot(regs)) { + } else if (!delay_slot(regs)) { regs->cp0_epc += 4; return 0; } diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c index e9fed8ca9b42..71e8f4c0b8da 100644 --- a/arch/mips/kernel/branch.c +++ b/arch/mips/kernel/branch.c @@ -399,7 +399,7 @@ int __MIPS16e_compute_return_epc(struct pt_regs *regs) * * @regs: Pointer to pt_regs * @insn: branch instruction to decode - * @returns: -EFAULT on error and forces SIGBUS, and on success + * @returns: -EFAULT on error and forces SIGILL, and on success * returns 0 or BRANCH_LIKELY_TAKEN as appropriate after * evaluating the branch. * @@ -431,7 +431,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, /* Fall through */ case jr_op: if (NO_R6EMU && insn.r_format.func == jr_op) - goto sigill_r6; + goto sigill_r2r6; regs->cp0_epc = regs->regs[insn.r_format.rs]; break; } @@ -446,7 +446,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, switch (insn.i_format.rt) { case bltzl_op: if (NO_R6EMU) - goto sigill_r6; + goto sigill_r2r6; case bltz_op: if ((long)regs->regs[insn.i_format.rs] < 0) { epc = epc + 4 + (insn.i_format.simmediate << 2); @@ -459,7 +459,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case bgezl_op: if (NO_R6EMU) - goto sigill_r6; + goto sigill_r2r6; case bgez_op: if ((long)regs->regs[insn.i_format.rs] >= 0) { epc = epc + 4 + (insn.i_format.simmediate << 2); @@ -473,10 +473,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case bltzal_op: case bltzall_op: if (NO_R6EMU && (insn.i_format.rs || - insn.i_format.rt == bltzall_op)) { - ret = -SIGILL; - break; - } + insn.i_format.rt == bltzall_op)) + goto sigill_r2r6; regs->regs[31] = epc + 8; /* * OK we are here either because we hit a NAL @@ -507,10 +505,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case bgezal_op: case bgezall_op: if (NO_R6EMU && (insn.i_format.rs || - insn.i_format.rt == bgezall_op)) { - ret = -SIGILL; - break; - } + insn.i_format.rt == bgezall_op)) + goto sigill_r2r6; regs->regs[31] = epc + 8; /* * OK we are here either because we hit a BAL @@ -556,6 +552,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, /* * These are unconditional and in j_format. */ + case jalx_op: case jal_op: regs->regs[31] = regs->cp0_epc + 8; case j_op: @@ -573,7 +570,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, */ case beql_op: if (NO_R6EMU) - goto sigill_r6; + goto sigill_r2r6; case beq_op: if (regs->regs[insn.i_format.rs] == regs->regs[insn.i_format.rt]) { @@ -587,7 +584,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case bnel_op: if (NO_R6EMU) - goto sigill_r6; + goto sigill_r2r6; case bne_op: if (regs->regs[insn.i_format.rs] != regs->regs[insn.i_format.rt]) { @@ -601,7 +598,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case blezl_op: /* not really i_format */ if (!insn.i_format.rt && NO_R6EMU) - goto sigill_r6; + goto sigill_r2r6; case blez_op: /* * Compact branches for R6 for the @@ -636,7 +633,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case bgtzl_op: if (!insn.i_format.rt && NO_R6EMU) - goto sigill_r6; + goto sigill_r2r6; case bgtz_op: /* * Compact branches for R6 for the @@ -843,11 +840,12 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, return ret; sigill_dsp: - printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); - force_sig(SIGBUS, current); + pr_info("%s: DSP branch but not DSP ASE - sending SIGILL.\n", + current->comm); + force_sig(SIGILL, current); return -EFAULT; -sigill_r6: - pr_info("%s: R2 branch but r2-to-r6 emulator is not preset - sending SIGILL.\n", +sigill_r2r6: + pr_info("%s: R2 branch but r2-to-r6 emulator is not present - sending SIGILL.\n", current->comm); force_sig(SIGILL, current); return -EFAULT; diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 298b2b773d12..f1fab6ff53e6 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -83,7 +83,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) } seq_printf(m, "isa\t\t\t:"); - if (cpu_has_mips_r1) + if (cpu_has_mips_1) seq_printf(m, " mips1"); if (cpu_has_mips_2) seq_printf(m, "%s", " mips2"); diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index c95bf18260f8..24c115a0721a 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -927,7 +927,7 @@ asmlinkage void syscall_trace_leave(struct pt_regs *regs) audit_syscall_exit(regs); if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) - trace_sys_exit(regs, regs->regs[2]); + trace_sys_exit(regs, regs_return_value(regs)); if (test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall_exit(regs, 0); diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index 2d23c834ba96..29b0c5f978e4 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -372,7 +372,7 @@ EXPORT(sys_call_table) PTR sys_writev PTR sys_cacheflush PTR sys_cachectl - PTR sys_sysmips + PTR __sys_sysmips PTR sys_ni_syscall /* 4150 */ PTR sys_getsid PTR sys_fdatasync diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index deac63315d0e..a6323a969919 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -312,7 +312,7 @@ EXPORT(sys_call_table) PTR sys_sched_getaffinity PTR sys_cacheflush PTR sys_cachectl - PTR sys_sysmips + PTR __sys_sysmips PTR sys_io_setup /* 5200 */ PTR sys_io_destroy PTR sys_io_getevents diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index ee93d5fe61d7..e0fdca8d3abe 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -298,7 +298,7 @@ EXPORT(sysn32_call_table) PTR compat_sys_sched_getaffinity PTR sys_cacheflush PTR sys_cachectl - PTR sys_sysmips + PTR __sys_sysmips PTR compat_sys_io_setup /* 6200 */ PTR sys_io_destroy PTR compat_sys_io_getevents diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index b77052ec6fb2..87c697181d25 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -367,7 +367,7 @@ EXPORT(sys32_call_table) PTR compat_sys_writev PTR sys_cacheflush PTR sys_cachectl - PTR sys_sysmips + PTR __sys_sysmips PTR sys_ni_syscall /* 4150 */ PTR sys_getsid PTR sys_fdatasync diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 53a7ef9a8f32..4234b2d726c5 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -28,6 +28,7 @@ #include <linux/elf.h> #include <asm/asm.h> +#include <asm/asm-eva.h> #include <asm/branch.h> #include <asm/cachectl.h> #include <asm/cacheflush.h> @@ -138,10 +139,12 @@ static inline int mips_atomic_set(unsigned long addr, unsigned long new) __asm__ __volatile__ ( " .set "MIPS_ISA_ARCH_LEVEL" \n" " li %[err], 0 \n" - "1: ll %[old], (%[addr]) \n" + "1: \n" + user_ll("%[old]", "(%[addr])") " move %[tmp], %[new] \n" - "2: sc %[tmp], (%[addr]) \n" - " bnez %[tmp], 4f \n" + "2: \n" + user_sc("%[tmp]", "(%[addr])") + " beqz %[tmp], 4f \n" "3: \n" " .insn \n" " .subsection 2 \n" @@ -199,6 +202,12 @@ static inline int mips_atomic_set(unsigned long addr, unsigned long new) unreachable(); } +/* + * mips_atomic_set() normally returns directly via syscall_exit potentially + * clobbering static registers, so be sure to preserve them. + */ +save_static_function(sys_sysmips); + SYSCALL_DEFINE3(sysmips, long, cmd, long, arg1, long, arg2) { switch (cmd) { diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index 734a2c7665ec..6da2e4a6ba39 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -2496,6 +2496,35 @@ dcopuop: return 0; } +/* + * Emulate FPU instructions. + * + * If we use FPU hardware, then we have been typically called to handle + * an unimplemented operation, such as where an operand is a NaN or + * denormalized. In that case exit the emulation loop after a single + * iteration so as to let hardware execute any subsequent instructions. + * + * If we have no FPU hardware or it has been disabled, then continue + * emulating floating-point instructions until one of these conditions + * has occurred: + * + * - a non-FPU instruction has been encountered, + * + * - an attempt to emulate has ended with a signal, + * + * - the ISA mode has been switched. + * + * We need to terminate the emulation loop if we got switched to the + * MIPS16 mode, whether supported or not, so that we do not attempt + * to emulate a MIPS16 instruction as a regular MIPS FPU instruction. + * Similarly if we got switched to the microMIPS mode and only the + * regular MIPS mode is supported, so that we do not attempt to emulate + * a microMIPS instruction as a regular MIPS FPU instruction. Or if + * we got switched to the regular MIPS mode and only the microMIPS mode + * is supported, so that we do not attempt to emulate a regular MIPS + * instruction that should cause an Address Error exception instead. + * For simplicity we always terminate upon an ISA mode switch. + */ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, int has_fpu, void *__user *fault_addr) { @@ -2581,6 +2610,15 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, break; if (sig) break; + /* + * We have to check for the ISA bit explicitly here, + * because `get_isa16_mode' may return 0 if support + * for code compression has been globally disabled, + * or otherwise we may produce the wrong signal or + * even proceed successfully where we must not. + */ + if ((xcp->cp0_epc ^ prevepc) & 0x1) + break; cond_resched(); } while (xcp->cp0_epc > prevepc); diff --git a/arch/openrisc/kernel/vmlinux.lds.S b/arch/openrisc/kernel/vmlinux.lds.S index 2d69a853b742..3a08b55609b6 100644 --- a/arch/openrisc/kernel/vmlinux.lds.S +++ b/arch/openrisc/kernel/vmlinux.lds.S @@ -38,6 +38,8 @@ SECTIONS /* Read-only sections, merged into text segment: */ . = LOAD_BASE ; + _text = .; + /* _s_kernel_ro must be page aligned */ . = ALIGN(PAGE_SIZE); _s_kernel_ro = .; diff --git a/arch/parisc/include/asm/dma-mapping.h b/arch/parisc/include/asm/dma-mapping.h index d8d60a57183f..f53725202955 100644 --- a/arch/parisc/include/asm/dma-mapping.h +++ b/arch/parisc/include/asm/dma-mapping.h @@ -39,6 +39,8 @@ struct hppa_dma_ops { ** flush/purge and allocate "regular" cacheable pages for everything. */ +#define DMA_ERROR_CODE (~(dma_addr_t)0) + #ifdef CONFIG_PA11 extern struct hppa_dma_ops pcxl_dma_ops; extern struct hppa_dma_ops pcx_dma_ops; @@ -209,12 +211,13 @@ parisc_walk_tree(struct device *dev) break; } } - BUG_ON(!dev->platform_data); return dev->platform_data; } - -#define GET_IOC(dev) (HBA_DATA(parisc_walk_tree(dev))->iommu) - + +#define GET_IOC(dev) ({ \ + void *__pdata = parisc_walk_tree(dev); \ + __pdata ? HBA_DATA(__pdata)->iommu : NULL; \ +}) #ifdef CONFIG_IOMMU_CCIO struct parisc_device; diff --git a/arch/parisc/include/asm/mmu_context.h b/arch/parisc/include/asm/mmu_context.h index 59be25764433..a81226257878 100644 --- a/arch/parisc/include/asm/mmu_context.h +++ b/arch/parisc/include/asm/mmu_context.h @@ -49,15 +49,26 @@ static inline void load_context(mm_context_t context) mtctl(__space_to_prot(context), 8); } -static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) +static inline void switch_mm_irqs_off(struct mm_struct *prev, + struct mm_struct *next, struct task_struct *tsk) { - if (prev != next) { mtctl(__pa(next->pgd), 25); load_context(next->context); } } +static inline void switch_mm(struct mm_struct *prev, + struct mm_struct *next, struct task_struct *tsk) +{ + unsigned long flags; + + local_irq_save(flags); + switch_mm_irqs_off(prev, next, tsk); + local_irq_restore(flags); +} +#define switch_mm_irqs_off switch_mm_irqs_off + #define deactivate_mm(tsk,mm) do { } while (0) static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next) diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index d4ffcfbc9885..041e1f9ec129 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -361,7 +361,7 @@ ENTRY_SAME(ni_syscall) /* 263: reserved for vserver */ ENTRY_SAME(add_key) ENTRY_SAME(request_key) /* 265 */ - ENTRY_SAME(keyctl) + ENTRY_COMP(keyctl) ENTRY_SAME(ioprio_set) ENTRY_SAME(ioprio_get) ENTRY_SAME(inotify_init) diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index 16dbe81c97c9..2f33a67bc531 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c @@ -298,7 +298,7 @@ bad_area: case 15: /* Data TLB miss fault/Data page fault */ /* send SIGSEGV when outside of vma */ if (!vma || - address < vma->vm_start || address > vma->vm_end) { + address < vma->vm_start || address >= vma->vm_end) { si.si_signo = SIGSEGV; si.si_code = SEGV_MAPERR; break; diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h index 55f106ed12bf..039c4b910615 100644 --- a/arch/powerpc/include/asm/atomic.h +++ b/arch/powerpc/include/asm/atomic.h @@ -460,7 +460,7 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u) * Atomically increments @v by 1, so long as @v is non-zero. * Returns non-zero if @v was non-zero, and zero otherwise. */ -static __inline__ long atomic64_inc_not_zero(atomic64_t *v) +static __inline__ int atomic64_inc_not_zero(atomic64_t *v) { long t1, t2; @@ -479,7 +479,7 @@ static __inline__ long atomic64_inc_not_zero(atomic64_t *v) : "r" (&v->counter) : "cc", "xer", "memory"); - return t1; + return t1 != 0; } #endif /* __powerpc64__ */ diff --git a/arch/powerpc/include/asm/elf.h b/arch/powerpc/include/asm/elf.h index ee46ffef608e..743ad7a400d6 100644 --- a/arch/powerpc/include/asm/elf.h +++ b/arch/powerpc/include/asm/elf.h @@ -23,12 +23,13 @@ #define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE PAGE_SIZE -/* This is the location that an ET_DYN program is loaded if exec'ed. Typical - use of this is to invoke "./ld.so someprog" to test out a new version of - the loader. We need to make sure that it is out of the way of the program - that it will "exec", and that there is sufficient room for the brk. */ - -#define ELF_ET_DYN_BASE 0x20000000 +/* + * This is the base location for PIE (ET_DYN with INTERP) loads. On + * 64-bit, this is raised to 4GB to leave the entire 32-bit address + * space open for things that want to use the area for 32-bit pointers. + */ +#define ELF_ET_DYN_BASE (is_32bit_task() ? 0x000400000UL : \ + 0x100000000UL) #define ELF_CORE_EFLAGS (is_elf2_task() ? 2 : 0) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 627d129d7fcb..ca372bbc0ffe 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -1236,7 +1236,7 @@ static inline unsigned long mfvtb (void) " .llong 0\n" \ ".previous" \ : "=r" (rval) \ - : "i" (CPU_FTR_CELL_TB_BUG), "i" (SPRN_TBRL)); \ + : "i" (CPU_FTR_CELL_TB_BUG), "i" (SPRN_TBRL) : "cr0"); \ rval;}) #else #define mftb() ({unsigned long rval; \ diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 329771559cbb..8b3b46b7b0f2 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -44,22 +44,8 @@ extern void __init dump_numa_cpu_topology(void); extern int sysfs_add_device_to_node(struct device *dev, int nid); extern void sysfs_remove_device_from_node(struct device *dev, int nid); -static inline int early_cpu_to_node(int cpu) -{ - int nid; - - nid = numa_cpu_lookup_table[cpu]; - - /* - * Fall back to node 0 if nid is unset (it should be, except bugs). - * This allows callers to safely do NODE_DATA(early_cpu_to_node(cpu)). - */ - return (nid < 0) ? 0 : nid; -} #else -static inline int early_cpu_to_node(int cpu) { return 0; } - static inline void dump_numa_cpu_topology(void) {} static inline int sysfs_add_device_to_node(struct device *dev, int nid) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index fe6e800c1357..a20823210ac0 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -751,7 +751,7 @@ void __init setup_arch(char **cmdline_p) static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size, size_t align) { - return __alloc_bootmem_node(NODE_DATA(early_cpu_to_node(cpu)), size, align, + return __alloc_bootmem_node(NODE_DATA(cpu_to_node(cpu)), size, align, __pa(MAX_DMA_ADDRESS)); } @@ -762,7 +762,7 @@ static void __init pcpu_fc_free(void *ptr, size_t size) static int pcpu_cpu_distance(unsigned int from, unsigned int to) { - if (early_cpu_to_node(from) == early_cpu_to_node(to)) + if (cpu_to_node(from) == cpu_to_node(to)) return LOCAL_DISTANCE; else return REMOTE_DISTANCE; diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 396dc44e783b..428563b195c3 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -2687,6 +2687,10 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu) { int r; int srcu_idx; + unsigned long ebb_regs[3] = {}; /* shut up GCC */ + unsigned long user_tar = 0; + unsigned long proc_fscr = 0; + unsigned int user_vrsave; if (!vcpu->arch.sane) { run->exit_reason = KVM_EXIT_INTERNAL_ERROR; @@ -2707,10 +2711,11 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu) run->fail_entry.hardware_entry_failure_reason = 0; return -EINVAL; } + /* Enable TM so we can read the TM SPRs */ + mtmsr(mfmsr() | MSR_TM); current->thread.tm_tfhar = mfspr(SPRN_TFHAR); current->thread.tm_tfiar = mfspr(SPRN_TFIAR); current->thread.tm_texasr = mfspr(SPRN_TEXASR); - current->thread.regs->msr &= ~MSR_TM; } #endif @@ -2736,6 +2741,17 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu) flush_fp_to_thread(current); flush_altivec_to_thread(current); flush_vsx_to_thread(current); + + /* Save userspace EBB and other register values */ + if (cpu_has_feature(CPU_FTR_ARCH_207S)) { + ebb_regs[0] = mfspr(SPRN_EBBHR); + ebb_regs[1] = mfspr(SPRN_EBBRR); + ebb_regs[2] = mfspr(SPRN_BESCR); + user_tar = mfspr(SPRN_TAR); + proc_fscr = mfspr(SPRN_FSCR); + } + user_vrsave = mfspr(SPRN_VRSAVE); + vcpu->arch.wqp = &vcpu->arch.vcore->wq; vcpu->arch.pgdir = current->mm->pgd; vcpu->arch.state = KVMPPC_VCPU_BUSY_IN_HOST; @@ -2757,6 +2773,29 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu) } } while (is_kvmppc_resume_guest(r)); + /* Restore userspace EBB and other register values */ + if (cpu_has_feature(CPU_FTR_ARCH_207S)) { + mtspr(SPRN_EBBHR, ebb_regs[0]); + mtspr(SPRN_EBBRR, ebb_regs[1]); + mtspr(SPRN_BESCR, ebb_regs[2]); + mtspr(SPRN_TAR, user_tar); + mtspr(SPRN_FSCR, proc_fscr); + } + mtspr(SPRN_VRSAVE, user_vrsave); + + /* + * Since we don't do lazy TM reload, we need to reload + * the TM registers here. + */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (cpu_has_feature(CPU_FTR_TM) && current->thread.regs && + (current->thread.regs->msr & MSR_TM)) { + mtspr(SPRN_TFHAR, current->thread.tm_tfhar); + mtspr(SPRN_TFIAR, current->thread.tm_tfiar); + mtspr(SPRN_TEXASR, current->thread.tm_texasr); + } +#endif + out: vcpu->arch.state = KVMPPC_VCPU_NOTREADY; atomic_dec(&vcpu->kvm->arch.vcpus_running); diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 1a743f87b37d..ffab9269bfe4 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -36,6 +36,13 @@ #define NAPPING_CEDE 1 #define NAPPING_NOVCPU 2 +/* Stack frame offsets for kvmppc_hv_entry */ +#define SFS 112 +#define STACK_SLOT_TRAP (SFS-4) +#define STACK_SLOT_CIABR (SFS-16) +#define STACK_SLOT_DAWR (SFS-24) +#define STACK_SLOT_DAWRX (SFS-32) + /* * Call kvmppc_hv_entry in real mode. * Must be called with interrupts hard-disabled. @@ -274,10 +281,10 @@ kvm_novcpu_exit: bl kvmhv_accumulate_time #endif 13: mr r3, r12 - stw r12, 112-4(r1) + stw r12, STACK_SLOT_TRAP(r1) bl kvmhv_commence_exit nop - lwz r12, 112-4(r1) + lwz r12, STACK_SLOT_TRAP(r1) b kvmhv_switch_to_host /* @@ -489,7 +496,7 @@ kvmppc_hv_entry: */ mflr r0 std r0, PPC_LR_STKOFF(r1) - stdu r1, -112(r1) + stdu r1, -SFS(r1) /* Save R1 in the PACA */ std r1, HSTATE_HOST_R1(r13) @@ -643,6 +650,16 @@ kvmppc_got_guest: mtspr SPRN_PURR,r7 mtspr SPRN_SPURR,r8 + /* Save host values of some registers */ +BEGIN_FTR_SECTION + mfspr r5, SPRN_CIABR + mfspr r6, SPRN_DAWR + mfspr r7, SPRN_DAWRX + std r5, STACK_SLOT_CIABR(r1) + std r6, STACK_SLOT_DAWR(r1) + std r7, STACK_SLOT_DAWRX(r1) +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) + BEGIN_FTR_SECTION /* Set partition DABR */ /* Do this before re-enabling PMU to avoid P7 DABR corruption bug */ @@ -1266,8 +1283,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S) */ li r0, 0 mtspr SPRN_IAMR, r0 - mtspr SPRN_CIABR, r0 - mtspr SPRN_DAWRX, r0 + mtspr SPRN_PSPB, r0 mtspr SPRN_TCSCR, r0 mtspr SPRN_WORT, r0 /* Set MMCRS to 1<<31 to freeze and disable the SPMC counters */ @@ -1283,6 +1299,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S) std r6,VCPU_UAMOR(r9) li r6,0 mtspr SPRN_AMR,r6 + mtspr SPRN_UAMOR, r6 /* Switch DSCR back to host value */ mfspr r8, SPRN_DSCR @@ -1424,6 +1441,16 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) slbia ptesync + /* Restore host values of some registers */ +BEGIN_FTR_SECTION + ld r5, STACK_SLOT_CIABR(r1) + ld r6, STACK_SLOT_DAWR(r1) + ld r7, STACK_SLOT_DAWRX(r1) + mtspr SPRN_CIABR, r5 + mtspr SPRN_DAWR, r6 + mtspr SPRN_DAWRX, r7 +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) + /* * POWER7/POWER8 guest -> host partition switch code. * We don't have to lock against tlbies but we do @@ -1533,8 +1560,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) li r0, KVM_GUEST_MODE_NONE stb r0, HSTATE_IN_GUEST(r13) - ld r0, 112+PPC_LR_STKOFF(r1) - addi r1, r1, 112 + ld r0, SFS+PPC_LR_STKOFF(r1) + addi r1, r1, SFS mtlr r0 blr diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index 4014881e9843..e37162d356d8 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -687,8 +687,10 @@ int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs, case 19: switch ((instr >> 1) & 0x3ff) { case 0: /* mcrf */ - rd = (instr >> 21) & 0x1c; - ra = (instr >> 16) & 0x1c; + rd = 7 - ((instr >> 23) & 0x7); + ra = 7 - ((instr >> 18) & 0x7); + rd *= 4; + ra *= 4; val = (regs->ccr >> ra) & 0xf; regs->ccr = (regs->ccr & ~(0xfUL << rd)) | (val << rd); goto instr_done; @@ -967,6 +969,19 @@ int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs, #endif case 19: /* mfcr */ + if ((instr >> 20) & 1) { + imm = 0xf0000000UL; + for (sh = 0; sh < 8; ++sh) { + if (instr & (0x80000 >> sh)) { + regs->gpr[rd] = regs->ccr & imm; + break; + } + imm >>= 4; + } + + goto instr_done; + } + regs->gpr[rd] = regs->ccr; regs->gpr[rd] &= 0xffffffffUL; goto instr_done; diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 7c7fcc042549..fb695f142563 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -82,7 +82,6 @@ static int pSeries_reconfig_remove_node(struct device_node *np) of_detach_node(np); of_node_put(parent); - of_node_put(np); /* Must decrement the refcount */ return 0; } diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h index bab6739a1154..b9eb7b1a49d2 100644 --- a/arch/s390/include/asm/elf.h +++ b/arch/s390/include/asm/elf.h @@ -154,14 +154,13 @@ extern unsigned int vdso_enabled; #define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE 4096 -/* This is the location that an ET_DYN program is loaded if exec'ed. Typical - use of this is to invoke "./ld.so someprog" to test out a new version of - the loader. We need to make sure that it is out of the way of the program - that it will "exec", and that there is sufficient room for the brk. 64-bit - tasks are aligned to 4GB. */ -#define ELF_ET_DYN_BASE (is_32bit_task() ? \ - (STACK_TOP / 3 * 2) : \ - (STACK_TOP / 3 * 2) & ~((1UL << 32) - 1)) +/* + * This is the base location for PIE (ET_DYN with INTERP) loads. On + * 64-bit, this is raised to 4GB to leave the entire 32-bit address + * space open for things that want to use the area for 32-bit pointers. + */ +#define ELF_ET_DYN_BASE (is_compat_task() ? 0x000400000UL : \ + 0x100000000UL) /* This yields a mask that user programs can use to figure out what instruction set this CPU supports. */ diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h index 6ba0bf928909..6bc941be6921 100644 --- a/arch/s390/include/asm/syscall.h +++ b/arch/s390/include/asm/syscall.h @@ -64,6 +64,12 @@ static inline void syscall_get_arguments(struct task_struct *task, { unsigned long mask = -1UL; + /* + * No arguments for this syscall, there's nothing to do. + */ + if (!n) + return; + BUG_ON(i + n > 6); #ifdef CONFIG_COMPAT if (test_tsk_thread_flag(task, TIF_31BIT)) diff --git a/arch/x86/crypto/sha1_ssse3_glue.c b/arch/x86/crypto/sha1_ssse3_glue.c index dd14616b7739..7de207a11014 100644 --- a/arch/x86/crypto/sha1_ssse3_glue.c +++ b/arch/x86/crypto/sha1_ssse3_glue.c @@ -201,7 +201,7 @@ asmlinkage void sha1_transform_avx2(u32 *digest, const char *data, static bool avx2_usable(void) { - if (avx_usable() && boot_cpu_has(X86_FEATURE_AVX2) + if (false && avx_usable() && boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_BMI1) && boot_cpu_has(X86_FEATURE_BMI2)) return true; diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h index d262f985bbc8..07cf288b692e 100644 --- a/arch/x86/include/asm/elf.h +++ b/arch/x86/include/asm/elf.h @@ -245,12 +245,13 @@ extern int force_personality32; #define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE 4096 -/* This is the location that an ET_DYN program is loaded if exec'ed. Typical - use of this is to invoke "./ld.so someprog" to test out a new version of - the loader. We need to make sure that it is out of the way of the program - that it will "exec", and that there is sufficient room for the brk. */ - -#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) +/* + * This is the base location for PIE (ET_DYN with INTERP) loads. On + * 64-bit, this is raised to 4GB to leave the entire 32-bit address + * space open for things that want to use the area for 32-bit pointers. + */ +#define ELF_ET_DYN_BASE (mmap_is_ia32() ? 0x000400000UL : \ + 0x100000000UL) /* This yields a mask that user programs can use to figure out what instruction set this CPU supports. This could be done in user space, diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 690b4027e17c..37db36fddc88 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -405,6 +405,8 @@ #define MSR_IA32_TSC_ADJUST 0x0000003b #define MSR_IA32_BNDCFGS 0x00000d90 +#define MSR_IA32_BNDCFGS_RSVD 0x00000ffc + #define MSR_IA32_XSS 0x00000da0 #define FEATURE_CONTROL_LOCKED (1<<0) diff --git a/arch/x86/include/asm/pat.h b/arch/x86/include/asm/pat.h index 0b1ff4c1c14e..fffb2794dd89 100644 --- a/arch/x86/include/asm/pat.h +++ b/arch/x86/include/asm/pat.h @@ -7,6 +7,7 @@ bool pat_enabled(void); void pat_disable(const char *reason); extern void pat_init(void); +extern void init_cache_modes(void); extern int reserve_memtype(u64 start, u64 end, enum page_cache_mode req_pcm, enum page_cache_mode *ret_pcm); diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h index 4c20dd333412..85133b2b8e99 100644 --- a/arch/x86/include/asm/xen/hypercall.h +++ b/arch/x86/include/asm/xen/hypercall.h @@ -43,6 +43,7 @@ #include <asm/page.h> #include <asm/pgtable.h> +#include <asm/smap.h> #include <xen/interface/xen.h> #include <xen/interface/sched.h> @@ -213,10 +214,12 @@ privcmd_call(unsigned call, __HYPERCALL_DECLS; __HYPERCALL_5ARG(a1, a2, a3, a4, a5); + stac(); asm volatile("call *%[call]" : __HYPERCALL_5PARAM : [call] "a" (&hypercall_page[call]) : __HYPERCALL_CLOBBER5); + clac(); return (long)__res; } diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index e75907601a41..1e5eb9f2ff5f 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -329,6 +329,14 @@ static void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger, struct mpc_intsrc mp_irq; /* + * Check bus_irq boundary. + */ + if (bus_irq >= NR_IRQS_LEGACY) { + pr_warn("Invalid bus_irq %u for legacy override\n", bus_irq); + return; + } + + /* * Convert 'gsi' to 'ioapic.pin'. */ ioapic = mp_find_ioapic(gsi); diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 1e5d2f07416b..fc91c98bee01 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -2115,7 +2115,7 @@ static inline void __init check_timer(void) int idx; idx = find_irq_entry(apic1, pin1, mp_INT); if (idx != -1 && irq_trigger(idx)) - unmask_ioapic_irq(irq_get_chip_data(0)); + unmask_ioapic_irq(irq_get_irq_data(0)); } irq_domain_deactivate_irq(irq_data); irq_domain_activate_irq(irq_data); diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c index 62aca448726a..2116176c1721 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c @@ -682,6 +682,9 @@ static int threshold_create_bank(unsigned int cpu, unsigned int bank) const char *name = th_names[bank]; int err = 0; + if (!dev) + return -ENODEV; + if (is_shared_bank(bank)) { nb = node_to_amd_nb(amd_get_nb_id(cpu)); diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index d2bbe343fda7..e67b834279b2 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1048,6 +1048,13 @@ void __init setup_arch(char **cmdline_p) if (mtrr_trim_uncached_memory(max_pfn)) max_pfn = e820_end_of_ram_pfn(); + /* + * This call is required when the CPU does not support PAT. If + * mtrr_bp_init() invoked it already via pat_init() the call has no + * effect. + */ + init_cache_modes(); + #ifdef CONFIG_X86_32 /* max_low_pfn get updated here */ find_low_pfn_range(); diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 9357b29de9bc..83d6369c45f5 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -46,11 +46,18 @@ static u32 xstate_required_size(u64 xstate_bv, bool compacted) return ret; } +bool kvm_mpx_supported(void) +{ + return ((host_xcr0 & (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR)) + && kvm_x86_ops->mpx_supported()); +} +EXPORT_SYMBOL_GPL(kvm_mpx_supported); + u64 kvm_supported_xcr0(void) { u64 xcr0 = KVM_SUPPORTED_XCR0 & host_xcr0; - if (!kvm_x86_ops->mpx_supported()) + if (!kvm_mpx_supported()) xcr0 &= ~(XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR); return xcr0; @@ -97,7 +104,7 @@ int kvm_update_cpuid(struct kvm_vcpu *vcpu) if (best && (best->eax & (F(XSAVES) | F(XSAVEC)))) best->ebx = xstate_required_size(vcpu->arch.xcr0, true); - vcpu->arch.eager_fpu = use_eager_fpu() || guest_cpuid_has_mpx(vcpu); + vcpu->arch.eager_fpu = use_eager_fpu(); if (vcpu->arch.eager_fpu) kvm_x86_ops->fpu_activate(vcpu); @@ -295,7 +302,7 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, #endif unsigned f_rdtscp = kvm_x86_ops->rdtscp_supported() ? F(RDTSCP) : 0; unsigned f_invpcid = kvm_x86_ops->invpcid_supported() ? F(INVPCID) : 0; - unsigned f_mpx = kvm_x86_ops->mpx_supported() ? F(MPX) : 0; + unsigned f_mpx = kvm_mpx_supported() ? F(MPX) : 0; unsigned f_xsaves = kvm_x86_ops->xsaves_supported() ? F(XSAVES) : 0; /* cpuid 1.edx */ diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index 3f5c48ddba45..d1534feefcfe 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -4,6 +4,7 @@ #include "x86.h" int kvm_update_cpuid(struct kvm_vcpu *vcpu); +bool kvm_mpx_supported(void); struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu, u32 function, u32 index); int kvm_dev_ioctl_get_cpuid(struct kvm_cpuid2 *cpuid, @@ -134,20 +135,20 @@ static inline bool guest_cpuid_has_rtm(struct kvm_vcpu *vcpu) return best && (best->ebx & bit(X86_FEATURE_RTM)); } -static inline bool guest_cpuid_has_mpx(struct kvm_vcpu *vcpu) +static inline bool guest_cpuid_has_pcommit(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; best = kvm_find_cpuid_entry(vcpu, 7, 0); - return best && (best->ebx & bit(X86_FEATURE_MPX)); + return best && (best->ebx & bit(X86_FEATURE_PCOMMIT)); } -static inline bool guest_cpuid_has_pcommit(struct kvm_vcpu *vcpu) +static inline bool guest_cpuid_has_mpx(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; best = kvm_find_cpuid_entry(vcpu, 7, 0); - return best && (best->ebx & bit(X86_FEATURE_PCOMMIT)); + return best && (best->ebx & bit(X86_FEATURE_MPX)); } static inline bool guest_cpuid_has_rdtscp(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index bbaa11f4e74b..b12391119ce8 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -863,7 +863,6 @@ static unsigned long nested_ept_get_cr3(struct kvm_vcpu *vcpu); static u64 construct_eptp(unsigned long root_hpa); static void kvm_cpu_vmxon(u64 addr); static void kvm_cpu_vmxoff(void); -static bool vmx_mpx_supported(void); static bool vmx_xsaves_supported(void); static int vmx_cpu_uses_apicv(struct kvm_vcpu *vcpu); static int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr); @@ -2541,7 +2540,7 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx) VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER | VM_EXIT_SAVE_VMX_PREEMPTION_TIMER | VM_EXIT_ACK_INTR_ON_EXIT; - if (vmx_mpx_supported()) + if (kvm_mpx_supported()) vmx->nested.nested_vmx_exit_ctls_high |= VM_EXIT_CLEAR_BNDCFGS; /* We support free control of debug control saving. */ @@ -2562,7 +2561,7 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx) VM_ENTRY_LOAD_IA32_PAT; vmx->nested.nested_vmx_entry_ctls_high |= (VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR | VM_ENTRY_LOAD_IA32_EFER); - if (vmx_mpx_supported()) + if (kvm_mpx_supported()) vmx->nested.nested_vmx_entry_ctls_high |= VM_ENTRY_LOAD_BNDCFGS; /* We support free control of debug control loading. */ @@ -2813,7 +2812,8 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) msr_info->data = vmcs_readl(GUEST_SYSENTER_ESP); break; case MSR_IA32_BNDCFGS: - if (!vmx_mpx_supported()) + if (!kvm_mpx_supported() || + (!msr_info->host_initiated && !guest_cpuid_has_mpx(vcpu))) return 1; msr_info->data = vmcs_read64(GUEST_BNDCFGS); break; @@ -2890,7 +2890,11 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) vmcs_writel(GUEST_SYSENTER_ESP, data); break; case MSR_IA32_BNDCFGS: - if (!vmx_mpx_supported()) + if (!kvm_mpx_supported() || + (!msr_info->host_initiated && !guest_cpuid_has_mpx(vcpu))) + return 1; + if (is_noncanonical_address(data & PAGE_MASK) || + (data & MSR_IA32_BNDCFGS_RSVD)) return 1; vmcs_write64(GUEST_BNDCFGS, data); break; @@ -3363,7 +3367,7 @@ static void init_vmcs_shadow_fields(void) for (i = j = 0; i < max_shadow_read_write_fields; i++) { switch (shadow_read_write_fields[i]) { case GUEST_BNDCFGS: - if (!vmx_mpx_supported()) + if (!kvm_mpx_supported()) continue; break; default: @@ -6253,7 +6257,6 @@ static __init int hardware_setup(void) vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_CS, false); vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_ESP, false); vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_EIP, false); - vmx_disable_intercept_for_msr(MSR_IA32_BNDCFGS, true); memcpy(vmx_msr_bitmap_legacy_x2apic, vmx_msr_bitmap_legacy, PAGE_SIZE); @@ -10265,7 +10268,7 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, vmcs12->guest_sysenter_cs = vmcs_read32(GUEST_SYSENTER_CS); vmcs12->guest_sysenter_esp = vmcs_readl(GUEST_SYSENTER_ESP); vmcs12->guest_sysenter_eip = vmcs_readl(GUEST_SYSENTER_EIP); - if (vmx_mpx_supported()) + if (kvm_mpx_supported()) vmcs12->guest_bndcfgs = vmcs_read64(GUEST_BNDCFGS); if (nested_cpu_has_xsaves(vmcs12)) vmcs12->xss_exit_bitmap = vmcs_read64(XSS_EXIT_BITMAP); diff --git a/arch/x86/lib/copy_user_64.S b/arch/x86/lib/copy_user_64.S index 27f89c79a44b..423644c230e7 100644 --- a/arch/x86/lib/copy_user_64.S +++ b/arch/x86/lib/copy_user_64.S @@ -80,7 +80,7 @@ ENTRY(copy_user_generic_unrolled) movl %edx,%ecx andl $63,%edx shrl $6,%ecx - jz 17f + jz .L_copy_short_string 1: movq (%rsi),%r8 2: movq 1*8(%rsi),%r9 3: movq 2*8(%rsi),%r10 @@ -101,7 +101,8 @@ ENTRY(copy_user_generic_unrolled) leaq 64(%rdi),%rdi decl %ecx jnz 1b -17: movl %edx,%ecx +.L_copy_short_string: + movl %edx,%ecx andl $7,%edx shrl $3,%ecx jz 20f @@ -215,6 +216,8 @@ ENDPROC(copy_user_generic_string) */ ENTRY(copy_user_enhanced_fast_string) ASM_STAC + cmpl $64,%edx + jb .L_copy_short_string /* less then 64 bytes, avoid the costly 'rep' */ movl %edx,%ecx 1: rep movsb diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c index 6ad687d104ca..3f1bb4f93a5a 100644 --- a/arch/x86/mm/pat.c +++ b/arch/x86/mm/pat.c @@ -36,14 +36,14 @@ #undef pr_fmt #define pr_fmt(fmt) "" fmt -static bool boot_cpu_done; - -static int __read_mostly __pat_enabled = IS_ENABLED(CONFIG_X86_PAT); -static void init_cache_modes(void); +static bool __read_mostly boot_cpu_done; +static bool __read_mostly pat_disabled = !IS_ENABLED(CONFIG_X86_PAT); +static bool __read_mostly pat_initialized; +static bool __read_mostly init_cm_done; void pat_disable(const char *reason) { - if (!__pat_enabled) + if (pat_disabled) return; if (boot_cpu_done) { @@ -51,10 +51,8 @@ void pat_disable(const char *reason) return; } - __pat_enabled = 0; + pat_disabled = true; pr_info("x86/PAT: %s\n", reason); - - init_cache_modes(); } static int __init nopat(char *str) @@ -66,7 +64,7 @@ early_param("nopat", nopat); bool pat_enabled(void) { - return !!__pat_enabled; + return pat_initialized; } EXPORT_SYMBOL_GPL(pat_enabled); @@ -204,6 +202,8 @@ static void __init_cache_modes(u64 pat) update_cache_mode_entry(i, cache); } pr_info("x86/PAT: Configuration [0-7]: %s\n", pat_msg); + + init_cm_done = true; } #define PAT(x, y) ((u64)PAT_ ## y << ((x)*8)) @@ -224,6 +224,7 @@ static void pat_bsp_init(u64 pat) } wrmsrl(MSR_IA32_CR_PAT, pat); + pat_initialized = true; __init_cache_modes(pat); } @@ -241,10 +242,9 @@ static void pat_ap_init(u64 pat) wrmsrl(MSR_IA32_CR_PAT, pat); } -static void init_cache_modes(void) +void init_cache_modes(void) { u64 pat = 0; - static int init_cm_done; if (init_cm_done) return; @@ -286,8 +286,6 @@ static void init_cache_modes(void) } __init_cache_modes(pat); - - init_cm_done = 1; } /** @@ -305,10 +303,8 @@ void pat_init(void) u64 pat; struct cpuinfo_x86 *c = &boot_cpu_data; - if (!pat_enabled()) { - init_cache_modes(); + if (pat_disabled) return; - } if ((c->x86_vendor == X86_VENDOR_INTEL) && (((c->x86 == 0x6) && (c->x86_model <= 0xd)) || diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c index 0c2fae8d929d..73eb7fd4aec4 100644 --- a/arch/x86/tools/relocs.c +++ b/arch/x86/tools/relocs.c @@ -992,11 +992,12 @@ static void emit_relocs(int as_text, int use_real_mode) die("Segment relocations found but --realmode not specified\n"); /* Order the relocations for more efficient processing */ - sort_relocs(&relocs16); sort_relocs(&relocs32); #if ELF_BITS == 64 sort_relocs(&relocs32neg); sort_relocs(&relocs64); +#else + sort_relocs(&relocs16); #endif /* Print the relocations */ diff --git a/crypto/authencesn.c b/crypto/authencesn.c index 0c0468869e25..52154ef21b5e 100644 --- a/crypto/authencesn.c +++ b/crypto/authencesn.c @@ -245,6 +245,9 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req, u8 *ihash = ohash + crypto_ahash_digestsize(auth); u32 tmp[2]; + if (!authsize) + goto decrypt; + /* Move high-order bits of sequence number back. */ scatterwalk_map_and_copy(tmp, dst, 4, 4, 0); scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0); @@ -253,6 +256,8 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req, if (crypto_memneq(ihash, ohash, authsize)) return -EBADMSG; +decrypt: + sg_init_table(areq_ctx->dst, 2); dst = scatterwalk_ffwd(areq_ctx->dst, dst, assoclen); diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 5ea5dc219f56..73c9c7fa9001 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -98,7 +98,15 @@ static int find_child_checks(struct acpi_device *adev, bool check_children) if (check_children && list_empty(&adev->children)) return -ENODEV; - return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE; + /* + * If the device has a _HID (or _CID) returning a valid ACPI/PNP + * device ID, it is better to make it look less attractive here, so that + * the other device with the same _ADR value (that may not have a valid + * device ID) can be matched going forward. [This means a second spec + * violation in a row, so whatever we do here is best effort anyway.] + */ + return sta_present && list_empty(&adev->pnp.ids) ? + FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE; } struct acpi_device *acpi_find_child_device(struct acpi_device *parent, diff --git a/drivers/android/Makefile b/drivers/android/Makefile index 3b7e4b072c58..4b7c726bb560 100644 --- a/drivers/android/Makefile +++ b/drivers/android/Makefile @@ -1,3 +1,3 @@ ccflags-y += -I$(src) # needed for trace events -obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o +obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 0c3cf182e351..71ebe36577c6 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -15,17 +15,49 @@ * */ +/* + * Locking overview + * + * There are 3 main spinlocks which must be acquired in the + * order shown: + * + * 1) proc->outer_lock : protects binder_ref + * binder_proc_lock() and binder_proc_unlock() are + * used to acq/rel. + * 2) node->lock : protects most fields of binder_node. + * binder_node_lock() and binder_node_unlock() are + * used to acq/rel + * 3) proc->inner_lock : protects the thread and node lists + * (proc->threads, proc->waiting_threads, proc->nodes) + * and all todo lists associated with the binder_proc + * (proc->todo, thread->todo, proc->delivered_death and + * node->async_todo), as well as thread->transaction_stack + * binder_inner_proc_lock() and binder_inner_proc_unlock() + * are used to acq/rel + * + * Any lock under procA must never be nested under any lock at the same + * level or below on procB. + * + * Functions that require a lock held on entry indicate which lock + * in the suffix of the function name: + * + * foo_olocked() : requires node->outer_lock + * foo_nlocked() : requires node->lock + * foo_ilocked() : requires proc->inner_lock + * foo_oilocked(): requires proc->outer_lock and proc->inner_lock + * foo_nilocked(): requires node->lock and proc->inner_lock + * ... + */ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <asm/cacheflush.h> -#include <linux/atomic.h> #include <linux/fdtable.h> #include <linux/file.h> #include <linux/freezer.h> #include <linux/fs.h> #include <linux/list.h> #include <linux/miscdevice.h> -#include <linux/mm.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/nsproxy.h> @@ -35,23 +67,32 @@ #include <linux/sched.h> #include <linux/seq_file.h> #include <linux/uaccess.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> #include <linux/pid_namespace.h> #include <linux/security.h> +#include <linux/spinlock.h> #ifdef CONFIG_ANDROID_BINDER_IPC_32BIT #define BINDER_IPC_32BIT 1 #endif #include <uapi/linux/android/binder.h> +#include "binder_alloc.h" #include "binder_trace.h" +static HLIST_HEAD(binder_deferred_list); +static DEFINE_MUTEX(binder_deferred_lock); + static HLIST_HEAD(binder_devices); +static HLIST_HEAD(binder_procs); +static DEFINE_MUTEX(binder_procs_lock); + +static HLIST_HEAD(binder_dead_nodes); +static DEFINE_SPINLOCK(binder_dead_nodes_lock); static struct dentry *binder_debugfs_dir_entry_root; static struct dentry *binder_debugfs_dir_entry_proc; -atomic_t binder_last_id; +static atomic_t binder_last_id; +static struct workqueue_struct *binder_deferred_workqueue; #define BINDER_DEBUG_ENTRY(name) \ static int binder_##name##_open(struct inode *inode, struct file *file) \ @@ -97,17 +138,13 @@ enum { BINDER_DEBUG_TRANSACTION_COMPLETE = 1U << 10, BINDER_DEBUG_FREE_BUFFER = 1U << 11, BINDER_DEBUG_INTERNAL_REFS = 1U << 12, - BINDER_DEBUG_BUFFER_ALLOC = 1U << 13, - BINDER_DEBUG_PRIORITY_CAP = 1U << 14, - BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 15, + BINDER_DEBUG_PRIORITY_CAP = 1U << 13, + BINDER_DEBUG_SPINLOCKS = 1U << 14, }; static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR | BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION; module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO); -static bool binder_debug_no_lock; -module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO); - static char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES; module_param_named(devices, binder_devices_param, charp, S_IRUGO); @@ -164,30 +201,27 @@ enum binder_stat_types { }; struct binder_stats { - int br[_IOC_NR(BR_FAILED_REPLY) + 1]; - int bc[_IOC_NR(BC_REPLY_SG) + 1]; -}; - -/* These are still global, since it's not always easy to get the context */ -struct binder_obj_stats { + atomic_t br[_IOC_NR(BR_FAILED_REPLY) + 1]; + atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1]; atomic_t obj_created[BINDER_STAT_COUNT]; atomic_t obj_deleted[BINDER_STAT_COUNT]; }; -static struct binder_obj_stats binder_obj_stats; +static struct binder_stats binder_stats; static inline void binder_stats_deleted(enum binder_stat_types type) { - atomic_inc(&binder_obj_stats.obj_deleted[type]); + atomic_inc(&binder_stats.obj_deleted[type]); } static inline void binder_stats_created(enum binder_stat_types type) { - atomic_inc(&binder_obj_stats.obj_created[type]); + atomic_inc(&binder_stats.obj_created[type]); } struct binder_transaction_log_entry { int debug_id; + int debug_id_done; int call_type; int from_proc; int from_thread; @@ -197,48 +231,45 @@ struct binder_transaction_log_entry { int to_node; int data_size; int offsets_size; + int return_error_line; + uint32_t return_error; + uint32_t return_error_param; const char *context_name; }; struct binder_transaction_log { - int next; - int full; + atomic_t cur; + bool full; struct binder_transaction_log_entry entry[32]; }; +static struct binder_transaction_log binder_transaction_log; +static struct binder_transaction_log binder_transaction_log_failed; static struct binder_transaction_log_entry *binder_transaction_log_add( struct binder_transaction_log *log) { struct binder_transaction_log_entry *e; + unsigned int cur = atomic_inc_return(&log->cur); - e = &log->entry[log->next]; - memset(e, 0, sizeof(*e)); - log->next++; - if (log->next == ARRAY_SIZE(log->entry)) { - log->next = 0; + if (cur >= ARRAY_SIZE(log->entry)) log->full = 1; - } + e = &log->entry[cur % ARRAY_SIZE(log->entry)]; + WRITE_ONCE(e->debug_id_done, 0); + /* + * write-barrier to synchronize access to e->debug_id_done. + * We make sure the initialized 0 value is seen before + * memset() other fields are zeroed by memset. + */ + smp_wmb(); + memset(e, 0, sizeof(*e)); return e; } struct binder_context { struct binder_node *binder_context_mgr_node; + struct mutex context_mgr_node_lock; + kuid_t binder_context_mgr_uid; const char *name; - - struct mutex binder_main_lock; - struct mutex binder_deferred_lock; - struct mutex binder_mmap_lock; - - struct hlist_head binder_procs; - struct hlist_head binder_dead_nodes; - struct hlist_head binder_deferred_list; - - struct work_struct deferred_work; - struct workqueue_struct *binder_deferred_workqueue; - struct binder_transaction_log transaction_log; - struct binder_transaction_log transaction_log_failed; - - struct binder_stats binder_stats; }; struct binder_device { @@ -247,11 +278,20 @@ struct binder_device { struct binder_context context; }; +/** + * struct binder_work - work enqueued on a worklist + * @entry: node enqueued on list + * @type: type of work to be performed + * + * There are separate work lists for proc, thread, and node (async). + */ struct binder_work { struct list_head entry; + enum { BINDER_WORK_TRANSACTION = 1, BINDER_WORK_TRANSACTION_COMPLETE, + BINDER_WORK_RETURN_ERROR, BINDER_WORK_NODE, BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, @@ -259,8 +299,76 @@ struct binder_work { } type; }; +struct binder_error { + struct binder_work work; + uint32_t cmd; +}; + +/** + * struct binder_node - binder node bookkeeping + * @debug_id: unique ID for debugging + * (invariant after initialized) + * @lock: lock for node fields + * @work: worklist element for node work + * (protected by @proc->inner_lock) + * @rb_node: element for proc->nodes tree + * (protected by @proc->inner_lock) + * @dead_node: element for binder_dead_nodes list + * (protected by binder_dead_nodes_lock) + * @proc: binder_proc that owns this node + * (invariant after initialized) + * @refs: list of references on this node + * (protected by @lock) + * @internal_strong_refs: used to take strong references when + * initiating a transaction + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @local_weak_refs: weak user refs from local process + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @local_strong_refs: strong user refs from local process + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @tmp_refs: temporary kernel refs + * (protected by @proc->inner_lock while @proc + * is valid, and by binder_dead_nodes_lock + * if @proc is NULL. During inc/dec and node release + * it is also protected by @lock to provide safety + * as the node dies and @proc becomes NULL) + * @ptr: userspace pointer for node + * (invariant, no lock needed) + * @cookie: userspace cookie for node + * (invariant, no lock needed) + * @has_strong_ref: userspace notified of strong ref + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @pending_strong_ref: userspace has acked notification of strong ref + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @has_weak_ref: userspace notified of weak ref + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @pending_weak_ref: userspace has acked notification of weak ref + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @has_async_transaction: async transaction to node in progress + * (protected by @lock) + * @sched_policy: minimum scheduling policy for node + * (invariant after initialized) + * @accept_fds: file descriptor operations supported for node + * (invariant after initialized) + * @min_priority: minimum scheduling priority + * (invariant after initialized) + * @inherit_rt: inherit RT scheduling policy from caller + * (invariant after initialized) + * @async_todo: list of async work items + * (protected by @proc->inner_lock) + * + * Bookkeeping structure for binder nodes. + */ struct binder_node { int debug_id; + spinlock_t lock; struct binder_work work; union { struct rb_node rb_node; @@ -271,88 +379,185 @@ struct binder_node { int internal_strong_refs; int local_weak_refs; int local_strong_refs; + int tmp_refs; binder_uintptr_t ptr; binder_uintptr_t cookie; - unsigned has_strong_ref:1; - unsigned pending_strong_ref:1; - unsigned has_weak_ref:1; - unsigned pending_weak_ref:1; - unsigned has_async_transaction:1; - unsigned accept_fds:1; - unsigned min_priority:8; + struct { + /* + * bitfield elements protected by + * proc inner_lock + */ + u8 has_strong_ref:1; + u8 pending_strong_ref:1; + u8 has_weak_ref:1; + u8 pending_weak_ref:1; + }; + struct { + /* + * invariant after initialization + */ + u8 sched_policy:2; + u8 inherit_rt:1; + u8 accept_fds:1; + u8 min_priority; + }; + bool has_async_transaction; struct list_head async_todo; }; struct binder_ref_death { + /** + * @work: worklist element for death notifications + * (protected by inner_lock of the proc that + * this ref belongs to) + */ struct binder_work work; binder_uintptr_t cookie; }; +/** + * struct binder_ref_data - binder_ref counts and id + * @debug_id: unique ID for the ref + * @desc: unique userspace handle for ref + * @strong: strong ref count (debugging only if not locked) + * @weak: weak ref count (debugging only if not locked) + * + * Structure to hold ref count and ref id information. Since + * the actual ref can only be accessed with a lock, this structure + * is used to return information about the ref to callers of + * ref inc/dec functions. + */ +struct binder_ref_data { + int debug_id; + uint32_t desc; + int strong; + int weak; +}; + +/** + * struct binder_ref - struct to track references on nodes + * @data: binder_ref_data containing id, handle, and current refcounts + * @rb_node_desc: node for lookup by @data.desc in proc's rb_tree + * @rb_node_node: node for lookup by @node in proc's rb_tree + * @node_entry: list entry for node->refs list in target node + * (protected by @node->lock) + * @proc: binder_proc containing ref + * @node: binder_node of target node. When cleaning up a + * ref for deletion in binder_cleanup_ref, a non-NULL + * @node indicates the node must be freed + * @death: pointer to death notification (ref_death) if requested + * (protected by @node->lock) + * + * Structure to track references from procA to target node (on procB). This + * structure is unsafe to access without holding @proc->outer_lock. + */ struct binder_ref { /* Lookups needed: */ /* node + proc => ref (transaction) */ /* desc + proc => ref (transaction, inc/dec ref) */ /* node => refs + procs (proc exit) */ - int debug_id; + struct binder_ref_data data; struct rb_node rb_node_desc; struct rb_node rb_node_node; struct hlist_node node_entry; struct binder_proc *proc; struct binder_node *node; - uint32_t desc; - int strong; - int weak; struct binder_ref_death *death; }; -struct binder_buffer { - struct list_head entry; /* free and allocated entries by address */ - struct rb_node rb_node; /* free entry by size or allocated entry */ - /* by address */ - unsigned free:1; - unsigned allow_user_free:1; - unsigned async_transaction:1; - unsigned debug_id:29; - - struct binder_transaction *transaction; - - struct binder_node *target_node; - size_t data_size; - size_t offsets_size; - size_t extra_buffers_size; - uint8_t data[0]; -}; - enum binder_deferred_state { BINDER_DEFERRED_PUT_FILES = 0x01, BINDER_DEFERRED_FLUSH = 0x02, BINDER_DEFERRED_RELEASE = 0x04, }; +/** + * struct binder_priority - scheduler policy and priority + * @sched_policy scheduler policy + * @prio [100..139] for SCHED_NORMAL, [0..99] for FIFO/RT + * + * The binder driver supports inheriting the following scheduler policies: + * SCHED_NORMAL + * SCHED_BATCH + * SCHED_FIFO + * SCHED_RR + */ +struct binder_priority { + unsigned int sched_policy; + int prio; +}; + +/** + * struct binder_proc - binder process bookkeeping + * @proc_node: element for binder_procs list + * @threads: rbtree of binder_threads in this proc + * (protected by @inner_lock) + * @nodes: rbtree of binder nodes associated with + * this proc ordered by node->ptr + * (protected by @inner_lock) + * @refs_by_desc: rbtree of refs ordered by ref->desc + * (protected by @outer_lock) + * @refs_by_node: rbtree of refs ordered by ref->node + * (protected by @outer_lock) + * @waiting_threads: threads currently waiting for proc work + * (protected by @inner_lock) + * @pid PID of group_leader of process + * (invariant after initialized) + * @tsk task_struct for group_leader of process + * (invariant after initialized) + * @files files_struct for process + * (invariant after initialized) + * @deferred_work_node: element for binder_deferred_list + * (protected by binder_deferred_lock) + * @deferred_work: bitmap of deferred work to perform + * (protected by binder_deferred_lock) + * @is_dead: process is dead and awaiting free + * when outstanding transactions are cleaned up + * (protected by @inner_lock) + * @todo: list of work for this process + * (protected by @inner_lock) + * @wait: wait queue head to wait for proc work + * (invariant after initialized) + * @stats: per-process binder statistics + * (atomics, no lock needed) + * @delivered_death: list of delivered death notification + * (protected by @inner_lock) + * @max_threads: cap on number of binder threads + * (protected by @inner_lock) + * @requested_threads: number of binder threads requested but not + * yet started. In current implementation, can + * only be 0 or 1. + * (protected by @inner_lock) + * @requested_threads_started: number binder threads started + * (protected by @inner_lock) + * @tmp_ref: temporary reference to indicate proc is in use + * (protected by @inner_lock) + * @default_priority: default scheduler priority + * (invariant after initialized) + * @debugfs_entry: debugfs node + * @alloc: binder allocator bookkeeping + * @context: binder_context for this proc + * (invariant after initialized) + * @inner_lock: can nest under outer_lock and/or node lock + * @outer_lock: no nesting under innor or node lock + * Lock order: 1) outer, 2) node, 3) inner + * + * Bookkeeping structure for binder processes + */ struct binder_proc { struct hlist_node proc_node; struct rb_root threads; struct rb_root nodes; struct rb_root refs_by_desc; struct rb_root refs_by_node; + struct list_head waiting_threads; int pid; - struct vm_area_struct *vma; - struct mm_struct *vma_vm_mm; struct task_struct *tsk; struct files_struct *files; struct hlist_node deferred_work_node; int deferred_work; - void *buffer; - ptrdiff_t user_buffer_offset; - - struct list_head buffers; - struct rb_root free_buffers; - struct rb_root allocated_buffers; - size_t free_async_space; + bool is_dead; - struct page **pages; - size_t buffer_size; - uint32_t buffer_free; struct list_head todo; wait_queue_head_t wait; struct binder_stats stats; @@ -360,10 +565,13 @@ struct binder_proc { int max_threads; int requested_threads; int requested_threads_started; - int ready_threads; - long default_priority; + int tmp_ref; + struct binder_priority default_priority; struct dentry *debugfs_entry; + struct binder_alloc alloc; struct binder_context *context; + spinlock_t inner_lock; + spinlock_t outer_lock; }; enum { @@ -372,22 +580,60 @@ enum { BINDER_LOOPER_STATE_EXITED = 0x04, BINDER_LOOPER_STATE_INVALID = 0x08, BINDER_LOOPER_STATE_WAITING = 0x10, - BINDER_LOOPER_STATE_NEED_RETURN = 0x20 + BINDER_LOOPER_STATE_POLL = 0x20, }; +/** + * struct binder_thread - binder thread bookkeeping + * @proc: binder process for this thread + * (invariant after initialization) + * @rb_node: element for proc->threads rbtree + * (protected by @proc->inner_lock) + * @waiting_thread_node: element for @proc->waiting_threads list + * (protected by @proc->inner_lock) + * @pid: PID for this thread + * (invariant after initialization) + * @looper: bitmap of looping state + * (only accessed by this thread) + * @looper_needs_return: looping thread needs to exit driver + * (no lock needed) + * @transaction_stack: stack of in-progress transactions for this thread + * (protected by @proc->inner_lock) + * @todo: list of work to do for this thread + * (protected by @proc->inner_lock) + * @return_error: transaction errors reported by this thread + * (only accessed by this thread) + * @reply_error: transaction errors reported by target thread + * (protected by @proc->inner_lock) + * @wait: wait queue for thread work + * @stats: per-thread statistics + * (atomics, no lock needed) + * @tmp_ref: temporary reference to indicate thread is in use + * (atomic since @proc->inner_lock cannot + * always be acquired) + * @is_dead: thread is dead and awaiting free + * when outstanding transactions are cleaned up + * (protected by @proc->inner_lock) + * @task: struct task_struct for this thread + * + * Bookkeeping structure for binder threads. + */ struct binder_thread { struct binder_proc *proc; struct rb_node rb_node; + struct list_head waiting_thread_node; int pid; - int looper; + int looper; /* only modified by this thread */ + bool looper_need_return; /* can be written by other thread */ struct binder_transaction *transaction_stack; struct list_head todo; - uint32_t return_error; /* Write failed, return error code in read buf */ - uint32_t return_error2; /* Write failed, return error code in read */ - /* buffer. Used when sending a reply to a dead process that */ - /* we are also waiting on */ + struct binder_error return_error; + struct binder_error reply_error; wait_queue_head_t wait; struct binder_stats stats; + atomic_t tmp_ref; + bool is_dead; + struct task_struct *task; }; struct binder_transaction { @@ -404,20 +650,263 @@ struct binder_transaction { struct binder_buffer *buffer; unsigned int code; unsigned int flags; - long priority; - long saved_priority; + struct binder_priority priority; + struct binder_priority saved_priority; + bool set_priority_called; kuid_t sender_euid; + /** + * @lock: protects @from, @to_proc, and @to_thread + * + * @from, @to_proc, and @to_thread can be set to NULL + * during thread teardown + */ + spinlock_t lock; }; +/** + * binder_proc_lock() - Acquire outer lock for given binder_proc + * @proc: struct binder_proc to acquire + * + * Acquires proc->outer_lock. Used to protect binder_ref + * structures associated with the given proc. + */ +#define binder_proc_lock(proc) _binder_proc_lock(proc, __LINE__) +static void +_binder_proc_lock(struct binder_proc *proc, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_lock(&proc->outer_lock); +} + +/** + * binder_proc_unlock() - Release spinlock for given binder_proc + * @proc: struct binder_proc to acquire + * + * Release lock acquired via binder_proc_lock() + */ +#define binder_proc_unlock(_proc) _binder_proc_unlock(_proc, __LINE__) +static void +_binder_proc_unlock(struct binder_proc *proc, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_unlock(&proc->outer_lock); +} + +/** + * binder_inner_proc_lock() - Acquire inner lock for given binder_proc + * @proc: struct binder_proc to acquire + * + * Acquires proc->inner_lock. Used to protect todo lists + */ +#define binder_inner_proc_lock(proc) _binder_inner_proc_lock(proc, __LINE__) +static void +_binder_inner_proc_lock(struct binder_proc *proc, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_lock(&proc->inner_lock); +} + +/** + * binder_inner_proc_unlock() - Release inner lock for given binder_proc + * @proc: struct binder_proc to acquire + * + * Release lock acquired via binder_inner_proc_lock() + */ +#define binder_inner_proc_unlock(proc) _binder_inner_proc_unlock(proc, __LINE__) +static void +_binder_inner_proc_unlock(struct binder_proc *proc, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_unlock(&proc->inner_lock); +} + +/** + * binder_node_lock() - Acquire spinlock for given binder_node + * @node: struct binder_node to acquire + * + * Acquires node->lock. Used to protect binder_node fields + */ +#define binder_node_lock(node) _binder_node_lock(node, __LINE__) +static void +_binder_node_lock(struct binder_node *node, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_lock(&node->lock); +} + +/** + * binder_node_unlock() - Release spinlock for given binder_proc + * @node: struct binder_node to acquire + * + * Release lock acquired via binder_node_lock() + */ +#define binder_node_unlock(node) _binder_node_unlock(node, __LINE__) +static void +_binder_node_unlock(struct binder_node *node, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_unlock(&node->lock); +} + +/** + * binder_node_inner_lock() - Acquire node and inner locks + * @node: struct binder_node to acquire + * + * Acquires node->lock. If node->proc also acquires + * proc->inner_lock. Used to protect binder_node fields + */ +#define binder_node_inner_lock(node) _binder_node_inner_lock(node, __LINE__) +static void +_binder_node_inner_lock(struct binder_node *node, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_lock(&node->lock); + if (node->proc) + binder_inner_proc_lock(node->proc); +} + +/** + * binder_node_unlock() - Release node and inner locks + * @node: struct binder_node to acquire + * + * Release lock acquired via binder_node_lock() + */ +#define binder_node_inner_unlock(node) _binder_node_inner_unlock(node, __LINE__) +static void +_binder_node_inner_unlock(struct binder_node *node, int line) +{ + struct binder_proc *proc = node->proc; + + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + if (proc) + binder_inner_proc_unlock(proc); + spin_unlock(&node->lock); +} + +static bool binder_worklist_empty_ilocked(struct list_head *list) +{ + return list_empty(list); +} + +/** + * binder_worklist_empty() - Check if no items on the work list + * @proc: binder_proc associated with list + * @list: list to check + * + * Return: true if there are no items on list, else false + */ +static bool binder_worklist_empty(struct binder_proc *proc, + struct list_head *list) +{ + bool ret; + + binder_inner_proc_lock(proc); + ret = binder_worklist_empty_ilocked(list); + binder_inner_proc_unlock(proc); + return ret; +} + +static void +binder_enqueue_work_ilocked(struct binder_work *work, + struct list_head *target_list) +{ + BUG_ON(target_list == NULL); + BUG_ON(work->entry.next && !list_empty(&work->entry)); + list_add_tail(&work->entry, target_list); +} + +/** + * binder_enqueue_work() - Add an item to the work list + * @proc: binder_proc associated with list + * @work: struct binder_work to add to list + * @target_list: list to add work to + * + * Adds the work to the specified list. Asserts that work + * is not already on a list. + */ +static void +binder_enqueue_work(struct binder_proc *proc, + struct binder_work *work, + struct list_head *target_list) +{ + binder_inner_proc_lock(proc); + binder_enqueue_work_ilocked(work, target_list); + binder_inner_proc_unlock(proc); +} + +static void +binder_dequeue_work_ilocked(struct binder_work *work) +{ + list_del_init(&work->entry); +} + +/** + * binder_dequeue_work() - Removes an item from the work list + * @proc: binder_proc associated with list + * @work: struct binder_work to remove from list + * + * Removes the specified work item from whatever list it is on. + * Can safely be called if work is not on any list. + */ +static void +binder_dequeue_work(struct binder_proc *proc, struct binder_work *work) +{ + binder_inner_proc_lock(proc); + binder_dequeue_work_ilocked(work); + binder_inner_proc_unlock(proc); +} + +static struct binder_work *binder_dequeue_work_head_ilocked( + struct list_head *list) +{ + struct binder_work *w; + + w = list_first_entry_or_null(list, struct binder_work, entry); + if (w) + list_del_init(&w->entry); + return w; +} + +/** + * binder_dequeue_work_head() - Dequeues the item at head of list + * @proc: binder_proc associated with list + * @list: list to dequeue head + * + * Removes the head of the list if there are items on the list + * + * Return: pointer dequeued binder_work, NULL if list was empty + */ +static struct binder_work *binder_dequeue_work_head( + struct binder_proc *proc, + struct list_head *list) +{ + struct binder_work *w; + + binder_inner_proc_lock(proc); + w = binder_dequeue_work_head_ilocked(list); + binder_inner_proc_unlock(proc); + return w; +} + static void binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer); +static void binder_free_thread(struct binder_thread *thread); +static void binder_free_proc(struct binder_proc *proc); +static void binder_inc_node_tmpref_ilocked(struct binder_node *node); static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) { struct files_struct *files = proc->files; unsigned long rlim_cur; unsigned long irqs; - int ret; if (files == NULL) return -ESRCH; @@ -428,11 +917,7 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE); unlock_task_sighand(proc->tsk, &irqs); - preempt_enable_no_resched(); - ret = __alloc_fd(files, 0, rlim_cur, flags); - preempt_disable(); - - return ret; + return __alloc_fd(files, 0, rlim_cur, flags); } /* @@ -441,11 +926,8 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) static void task_fd_install( struct binder_proc *proc, unsigned int fd, struct file *file) { - if (proc->files) { - preempt_enable_no_resched(); + if (proc->files) __fd_install(proc->files, fd, file); - preempt_disable(); - } } /* @@ -469,526 +951,281 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) return retval; } -static inline void binder_lock(struct binder_context *context, const char *tag) -{ - trace_binder_lock(tag); - mutex_lock(&context->binder_main_lock); - preempt_disable(); - trace_binder_locked(tag); -} - -static inline void binder_unlock(struct binder_context *context, - const char *tag) +static bool binder_has_work_ilocked(struct binder_thread *thread, + bool do_proc_work) { - trace_binder_unlock(tag); - mutex_unlock(&context->binder_main_lock); - preempt_enable(); + return !binder_worklist_empty_ilocked(&thread->todo) || + thread->looper_need_return || + (do_proc_work && + !binder_worklist_empty_ilocked(&thread->proc->todo)); } -static inline void *kzalloc_preempt_disabled(size_t size) +static bool binder_has_work(struct binder_thread *thread, bool do_proc_work) { - void *ptr; + bool has_work; - ptr = kzalloc(size, GFP_NOWAIT); - if (ptr) - return ptr; + binder_inner_proc_lock(thread->proc); + has_work = binder_has_work_ilocked(thread, do_proc_work); + binder_inner_proc_unlock(thread->proc); - preempt_enable_no_resched(); - ptr = kzalloc(size, GFP_KERNEL); - preempt_disable(); - - return ptr; -} - -static inline long copy_to_user_preempt_disabled(void __user *to, const void *from, long n) -{ - long ret; - - preempt_enable_no_resched(); - ret = copy_to_user(to, from, n); - preempt_disable(); - return ret; + return has_work; } -static inline long copy_from_user_preempt_disabled(void *to, const void __user *from, long n) +static bool binder_available_for_proc_work_ilocked(struct binder_thread *thread) { - long ret; - - preempt_enable_no_resched(); - ret = copy_from_user(to, from, n); - preempt_disable(); - return ret; + return !thread->transaction_stack && + binder_worklist_empty_ilocked(&thread->todo) && + (thread->looper & (BINDER_LOOPER_STATE_ENTERED | + BINDER_LOOPER_STATE_REGISTERED)); } -#define get_user_preempt_disabled(x, ptr) \ -({ \ - int __ret; \ - preempt_enable_no_resched(); \ - __ret = get_user(x, ptr); \ - preempt_disable(); \ - __ret; \ -}) - -#define put_user_preempt_disabled(x, ptr) \ -({ \ - int __ret; \ - preempt_enable_no_resched(); \ - __ret = put_user(x, ptr); \ - preempt_disable(); \ - __ret; \ -}) - -static void binder_set_nice(long nice) +static void binder_wakeup_poll_threads_ilocked(struct binder_proc *proc, + bool sync) { - long min_nice; + struct rb_node *n; + struct binder_thread *thread; - if (can_nice(current, nice)) { - set_user_nice(current, nice); - return; + for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) { + thread = rb_entry(n, struct binder_thread, rb_node); + if (thread->looper & BINDER_LOOPER_STATE_POLL && + binder_available_for_proc_work_ilocked(thread)) { + if (sync) + wake_up_interruptible_sync(&thread->wait); + else + wake_up_interruptible(&thread->wait); + } } - min_nice = rlimit_to_nice(current->signal->rlim[RLIMIT_NICE].rlim_cur); - binder_debug(BINDER_DEBUG_PRIORITY_CAP, - "%d: nice value %ld not allowed use %ld instead\n", - current->pid, nice, min_nice); - set_user_nice(current, min_nice); - if (min_nice <= MAX_NICE) - return; - binder_user_error("%d RLIMIT_NICE not set\n", current->pid); } -static size_t binder_buffer_size(struct binder_proc *proc, - struct binder_buffer *buffer) -{ - if (list_is_last(&buffer->entry, &proc->buffers)) - return proc->buffer + proc->buffer_size - (void *)buffer->data; - return (size_t)list_entry(buffer->entry.next, - struct binder_buffer, entry) - (size_t)buffer->data; -} - -static void binder_insert_free_buffer(struct binder_proc *proc, - struct binder_buffer *new_buffer) +/** + * binder_select_thread_ilocked() - selects a thread for doing proc work. + * @proc: process to select a thread from + * + * Note that calling this function moves the thread off the waiting_threads + * list, so it can only be woken up by the caller of this function, or a + * signal. Therefore, callers *should* always wake up the thread this function + * returns. + * + * Return: If there's a thread currently waiting for process work, + * returns that thread. Otherwise returns NULL. + */ +static struct binder_thread * +binder_select_thread_ilocked(struct binder_proc *proc) { - struct rb_node **p = &proc->free_buffers.rb_node; - struct rb_node *parent = NULL; - struct binder_buffer *buffer; - size_t buffer_size; - size_t new_buffer_size; - - BUG_ON(!new_buffer->free); - - new_buffer_size = binder_buffer_size(proc, new_buffer); - - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: add free buffer, size %zd, at %p\n", - proc->pid, new_buffer_size, new_buffer); + struct binder_thread *thread; - while (*p) { - parent = *p; - buffer = rb_entry(parent, struct binder_buffer, rb_node); - BUG_ON(!buffer->free); + assert_spin_locked(&proc->inner_lock); + thread = list_first_entry_or_null(&proc->waiting_threads, + struct binder_thread, + waiting_thread_node); - buffer_size = binder_buffer_size(proc, buffer); + if (thread) + list_del_init(&thread->waiting_thread_node); - if (new_buffer_size < buffer_size) - p = &parent->rb_left; - else - p = &parent->rb_right; - } - rb_link_node(&new_buffer->rb_node, parent, p); - rb_insert_color(&new_buffer->rb_node, &proc->free_buffers); + return thread; } -static void binder_insert_allocated_buffer(struct binder_proc *proc, - struct binder_buffer *new_buffer) +/** + * binder_wakeup_thread_ilocked() - wakes up a thread for doing proc work. + * @proc: process to wake up a thread in + * @thread: specific thread to wake-up (may be NULL) + * @sync: whether to do a synchronous wake-up + * + * This function wakes up a thread in the @proc process. + * The caller may provide a specific thread to wake-up in + * the @thread parameter. If @thread is NULL, this function + * will wake up threads that have called poll(). + * + * Note that for this function to work as expected, callers + * should first call binder_select_thread() to find a thread + * to handle the work (if they don't have a thread already), + * and pass the result into the @thread parameter. + */ +static void binder_wakeup_thread_ilocked(struct binder_proc *proc, + struct binder_thread *thread, + bool sync) { - struct rb_node **p = &proc->allocated_buffers.rb_node; - struct rb_node *parent = NULL; - struct binder_buffer *buffer; - - BUG_ON(new_buffer->free); - - while (*p) { - parent = *p; - buffer = rb_entry(parent, struct binder_buffer, rb_node); - BUG_ON(buffer->free); + assert_spin_locked(&proc->inner_lock); - if (new_buffer < buffer) - p = &parent->rb_left; - else if (new_buffer > buffer) - p = &parent->rb_right; + if (thread) { + if (sync) + wake_up_interruptible_sync(&thread->wait); else - BUG(); + wake_up_interruptible(&thread->wait); + return; } - rb_link_node(&new_buffer->rb_node, parent, p); - rb_insert_color(&new_buffer->rb_node, &proc->allocated_buffers); + + /* Didn't find a thread waiting for proc work; this can happen + * in two scenarios: + * 1. All threads are busy handling transactions + * In that case, one of those threads should call back into + * the kernel driver soon and pick up this work. + * 2. Threads are using the (e)poll interface, in which case + * they may be blocked on the waitqueue without having been + * added to waiting_threads. For this case, we just iterate + * over all threads not handling transaction work, and + * wake them all up. We wake all because we don't know whether + * a thread that called into (e)poll is handling non-binder + * work currently. + */ + binder_wakeup_poll_threads_ilocked(proc, sync); } -static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc, - uintptr_t user_ptr) +static void binder_wakeup_proc_ilocked(struct binder_proc *proc) { - struct rb_node *n = proc->allocated_buffers.rb_node; - struct binder_buffer *buffer; - struct binder_buffer *kern_ptr; - - kern_ptr = (struct binder_buffer *)(user_ptr - proc->user_buffer_offset - - offsetof(struct binder_buffer, data)); + struct binder_thread *thread = binder_select_thread_ilocked(proc); - while (n) { - buffer = rb_entry(n, struct binder_buffer, rb_node); - BUG_ON(buffer->free); - - if (kern_ptr < buffer) - n = n->rb_left; - else if (kern_ptr > buffer) - n = n->rb_right; - else - return buffer; - } - return NULL; + binder_wakeup_thread_ilocked(proc, thread, /* sync = */false); } -static int binder_update_page_range(struct binder_proc *proc, int allocate, - void *start, void *end, - struct vm_area_struct *vma) +static bool is_rt_policy(int policy) { - void *page_addr; - unsigned long user_page_addr; - struct page **page; - struct mm_struct *mm; - - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: %s pages %p-%p\n", proc->pid, - allocate ? "allocate" : "free", start, end); + return policy == SCHED_FIFO || policy == SCHED_RR; +} - if (end <= start) - return 0; +static bool is_fair_policy(int policy) +{ + return policy == SCHED_NORMAL || policy == SCHED_BATCH; +} - trace_binder_update_page_range(proc, allocate, start, end); +static bool binder_supported_policy(int policy) +{ + return is_fair_policy(policy) || is_rt_policy(policy); +} - if (vma) - mm = NULL; +static int to_userspace_prio(int policy, int kernel_priority) +{ + if (is_fair_policy(policy)) + return PRIO_TO_NICE(kernel_priority); else - mm = get_task_mm(proc->tsk); + return MAX_USER_RT_PRIO - 1 - kernel_priority; +} - preempt_enable_no_resched(); +static int to_kernel_prio(int policy, int user_priority) +{ + if (is_fair_policy(policy)) + return NICE_TO_PRIO(user_priority); + else + return MAX_USER_RT_PRIO - 1 - user_priority; +} - if (mm) { - down_write(&mm->mmap_sem); - vma = proc->vma; - if (vma && mm != proc->vma_vm_mm) { - pr_err("%d: vma mm and task mm mismatch\n", - proc->pid); - vma = NULL; - } - } +static void binder_do_set_priority(struct task_struct *task, + struct binder_priority desired, + bool verify) +{ + int priority; /* user-space prio value */ + bool has_cap_nice; + unsigned int policy = desired.sched_policy; - if (allocate == 0) - goto free_range; + if (task->policy == policy && task->normal_prio == desired.prio) + return; - if (vma == NULL) { - pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n", - proc->pid); - goto err_no_vma; - } + has_cap_nice = has_capability_noaudit(task, CAP_SYS_NICE); - for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { - int ret; + priority = to_userspace_prio(policy, desired.prio); - page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE]; + if (verify && is_rt_policy(policy) && !has_cap_nice) { + long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO); - BUG_ON(*page); - *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); - if (*page == NULL) { - pr_err("%d: binder_alloc_buf failed for page at %p\n", - proc->pid, page_addr); - goto err_alloc_page_failed; + if (max_rtprio == 0) { + policy = SCHED_NORMAL; + priority = MIN_NICE; + } else if (priority > max_rtprio) { + priority = max_rtprio; } - ret = map_kernel_range_noflush((unsigned long)page_addr, - PAGE_SIZE, PAGE_KERNEL, page); - flush_cache_vmap((unsigned long)page_addr, - (unsigned long)page_addr + PAGE_SIZE); - if (ret != 1) { - pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n", - proc->pid, page_addr); - goto err_map_kernel_failed; - } - user_page_addr = - (uintptr_t)page_addr + proc->user_buffer_offset; - ret = vm_insert_page(vma, user_page_addr, page[0]); - if (ret) { - pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n", - proc->pid, user_page_addr); - goto err_vm_insert_page_failed; - } - /* vm_insert_page does not seem to increment the refcount */ - } - if (mm) { - up_write(&mm->mmap_sem); - mmput(mm); } - preempt_disable(); + if (verify && is_fair_policy(policy) && !has_cap_nice) { + long min_nice = rlimit_to_nice(task_rlimit(task, RLIMIT_NICE)); - return 0; - -free_range: - for (page_addr = end - PAGE_SIZE; page_addr >= start; - page_addr -= PAGE_SIZE) { - page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE]; - if (vma) - zap_page_range(vma, (uintptr_t)page_addr + - proc->user_buffer_offset, PAGE_SIZE, NULL); -err_vm_insert_page_failed: - unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); -err_map_kernel_failed: - __free_page(*page); - *page = NULL; -err_alloc_page_failed: - ; - } -err_no_vma: - if (mm) { - up_write(&mm->mmap_sem); - mmput(mm); - } - - preempt_disable(); - - return -ENOMEM; -} - -static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, - size_t data_size, - size_t offsets_size, - size_t extra_buffers_size, - int is_async) -{ - struct rb_node *n = proc->free_buffers.rb_node; - struct binder_buffer *buffer; - size_t buffer_size; - struct rb_node *best_fit = NULL; - void *has_page_addr; - void *end_page_addr; - size_t size, data_offsets_size; - - if (proc->vma == NULL) { - pr_err("%d: binder_alloc_buf, no vma\n", - proc->pid); - return NULL; - } - - data_offsets_size = ALIGN(data_size, sizeof(void *)) + - ALIGN(offsets_size, sizeof(void *)); - - if (data_offsets_size < data_size || data_offsets_size < offsets_size) { - binder_user_error("%d: got transaction with invalid size %zd-%zd\n", - proc->pid, data_size, offsets_size); - return NULL; - } - size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *)); - if (size < data_offsets_size || size < extra_buffers_size) { - binder_user_error("%d: got transaction with invalid extra_buffers_size %zd\n", - proc->pid, extra_buffers_size); - return NULL; - } - if (is_async && - proc->free_async_space < size + sizeof(struct binder_buffer)) { - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: binder_alloc_buf size %zd failed, no async space left\n", - proc->pid, size); - return NULL; - } - - while (n) { - buffer = rb_entry(n, struct binder_buffer, rb_node); - BUG_ON(!buffer->free); - buffer_size = binder_buffer_size(proc, buffer); - - if (size < buffer_size) { - best_fit = n; - n = n->rb_left; - } else if (size > buffer_size) - n = n->rb_right; - else { - best_fit = n; - break; + if (min_nice > MAX_NICE) { + binder_user_error("%d RLIMIT_NICE not set\n", + task->pid); + return; + } else if (priority < min_nice) { + priority = min_nice; } } - if (best_fit == NULL) { - pr_err("%d: binder_alloc_buf size %zd failed, no address space\n", - proc->pid, size); - return NULL; - } - if (n == NULL) { - buffer = rb_entry(best_fit, struct binder_buffer, rb_node); - buffer_size = binder_buffer_size(proc, buffer); - } - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: binder_alloc_buf size %zd got buffer %p size %zd\n", - proc->pid, size, buffer, buffer_size); + if (policy != desired.sched_policy || + to_kernel_prio(policy, priority) != desired.prio) + binder_debug(BINDER_DEBUG_PRIORITY_CAP, + "%d: priority %d not allowed, using %d instead\n", + task->pid, desired.prio, + to_kernel_prio(policy, priority)); - has_page_addr = - (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK); - if (n == NULL) { - if (size + sizeof(struct binder_buffer) + 4 >= buffer_size) - buffer_size = size; /* no room for other buffers */ - else - buffer_size = size + sizeof(struct binder_buffer); - } - end_page_addr = - (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size); - if (end_page_addr > has_page_addr) - end_page_addr = has_page_addr; - if (binder_update_page_range(proc, 1, - (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL)) - return NULL; + /* Set the actual priority */ + if (task->policy != policy || is_rt_policy(policy)) { + struct sched_param params; - rb_erase(best_fit, &proc->free_buffers); - buffer->free = 0; - binder_insert_allocated_buffer(proc, buffer); - if (buffer_size != size) { - struct binder_buffer *new_buffer = (void *)buffer->data + size; + params.sched_priority = is_rt_policy(policy) ? priority : 0; - list_add(&new_buffer->entry, &buffer->entry); - new_buffer->free = 1; - binder_insert_free_buffer(proc, new_buffer); + sched_setscheduler_nocheck(task, + policy | SCHED_RESET_ON_FORK, + ¶ms); } - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: binder_alloc_buf size %zd got %p\n", - proc->pid, size, buffer); - buffer->data_size = data_size; - buffer->offsets_size = offsets_size; - buffer->extra_buffers_size = extra_buffers_size; - buffer->async_transaction = is_async; - if (is_async) { - proc->free_async_space -= size + sizeof(struct binder_buffer); - binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, - "%d: binder_alloc_buf size %zd async free %zd\n", - proc->pid, size, proc->free_async_space); - } - - return buffer; -} - -static void *buffer_start_page(struct binder_buffer *buffer) -{ - return (void *)((uintptr_t)buffer & PAGE_MASK); + if (is_fair_policy(policy)) + set_user_nice(task, priority); } -static void *buffer_end_page(struct binder_buffer *buffer) +static void binder_set_priority(struct task_struct *task, + struct binder_priority desired) { - return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK); + binder_do_set_priority(task, desired, /* verify = */ true); } -static void binder_delete_free_buffer(struct binder_proc *proc, - struct binder_buffer *buffer) +static void binder_restore_priority(struct task_struct *task, + struct binder_priority desired) { - struct binder_buffer *prev, *next = NULL; - int free_page_end = 1; - int free_page_start = 1; - - BUG_ON(proc->buffers.next == &buffer->entry); - prev = list_entry(buffer->entry.prev, struct binder_buffer, entry); - BUG_ON(!prev->free); - if (buffer_end_page(prev) == buffer_start_page(buffer)) { - free_page_start = 0; - if (buffer_end_page(prev) == buffer_end_page(buffer)) - free_page_end = 0; - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %p share page with %p\n", - proc->pid, buffer, prev); - } - - if (!list_is_last(&buffer->entry, &proc->buffers)) { - next = list_entry(buffer->entry.next, - struct binder_buffer, entry); - if (buffer_start_page(next) == buffer_end_page(buffer)) { - free_page_end = 0; - if (buffer_start_page(next) == - buffer_start_page(buffer)) - free_page_start = 0; - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %p share page with %p\n", - proc->pid, buffer, prev); - } - } - list_del(&buffer->entry); - if (free_page_start || free_page_end) { - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %p do not share page%s%s with %p or %p\n", - proc->pid, buffer, free_page_start ? "" : " end", - free_page_end ? "" : " start", prev, next); - binder_update_page_range(proc, 0, free_page_start ? - buffer_start_page(buffer) : buffer_end_page(buffer), - (free_page_end ? buffer_end_page(buffer) : - buffer_start_page(buffer)) + PAGE_SIZE, NULL); - } + binder_do_set_priority(task, desired, /* verify = */ false); } -static void binder_free_buf(struct binder_proc *proc, - struct binder_buffer *buffer) +static void binder_transaction_priority(struct task_struct *task, + struct binder_transaction *t, + struct binder_priority node_prio, + bool inherit_rt) { - size_t size, buffer_size; - - buffer_size = binder_buffer_size(proc, buffer); - - size = ALIGN(buffer->data_size, sizeof(void *)) + - ALIGN(buffer->offsets_size, sizeof(void *)) + - ALIGN(buffer->extra_buffers_size, sizeof(void *)); + struct binder_priority desired_prio; - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: binder_free_buf %p size %zd buffer_size %zd\n", - proc->pid, buffer, size, buffer_size); - - BUG_ON(buffer->free); - BUG_ON(size > buffer_size); - BUG_ON(buffer->transaction != NULL); - BUG_ON((void *)buffer < proc->buffer); - BUG_ON((void *)buffer > proc->buffer + proc->buffer_size); + if (t->set_priority_called) + return; - if (buffer->async_transaction) { - proc->free_async_space += size + sizeof(struct binder_buffer); + t->set_priority_called = true; + t->saved_priority.sched_policy = task->policy; + t->saved_priority.prio = task->normal_prio; - binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, - "%d: binder_free_buf size %zd async free %zd\n", - proc->pid, size, proc->free_async_space); + if (!inherit_rt && is_rt_policy(desired_prio.sched_policy)) { + desired_prio.prio = NICE_TO_PRIO(0); + desired_prio.sched_policy = SCHED_NORMAL; + } else { + desired_prio.prio = t->priority.prio; + desired_prio.sched_policy = t->priority.sched_policy; } - binder_update_page_range(proc, 0, - (void *)PAGE_ALIGN((uintptr_t)buffer->data), - (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK), - NULL); - rb_erase(&buffer->rb_node, &proc->allocated_buffers); - buffer->free = 1; - if (!list_is_last(&buffer->entry, &proc->buffers)) { - struct binder_buffer *next = list_entry(buffer->entry.next, - struct binder_buffer, entry); - - if (next->free) { - rb_erase(&next->rb_node, &proc->free_buffers); - binder_delete_free_buffer(proc, next); - } - } - if (proc->buffers.next != &buffer->entry) { - struct binder_buffer *prev = list_entry(buffer->entry.prev, - struct binder_buffer, entry); - - if (prev->free) { - binder_delete_free_buffer(proc, buffer); - rb_erase(&prev->rb_node, &proc->free_buffers); - buffer = prev; - } + if (node_prio.prio < t->priority.prio || + (node_prio.prio == t->priority.prio && + node_prio.sched_policy == SCHED_FIFO)) { + /* + * In case the minimum priority on the node is + * higher (lower value), use that priority. If + * the priority is the same, but the node uses + * SCHED_FIFO, prefer SCHED_FIFO, since it can + * run unbounded, unlike SCHED_RR. + */ + desired_prio = node_prio; } - binder_insert_free_buffer(proc, buffer); + + binder_set_priority(task, desired_prio); } -static struct binder_node *binder_get_node(struct binder_proc *proc, - binder_uintptr_t ptr) +static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc, + binder_uintptr_t ptr) { struct rb_node *n = proc->nodes.rb_node; struct binder_node *node; + assert_spin_locked(&proc->inner_lock); + while (n) { node = rb_entry(n, struct binder_node, rb_node); @@ -996,21 +1233,47 @@ static struct binder_node *binder_get_node(struct binder_proc *proc, n = n->rb_left; else if (ptr > node->ptr) n = n->rb_right; - else + else { + /* + * take an implicit weak reference + * to ensure node stays alive until + * call to binder_put_node() + */ + binder_inc_node_tmpref_ilocked(node); return node; + } } return NULL; } -static struct binder_node *binder_new_node(struct binder_proc *proc, - binder_uintptr_t ptr, - binder_uintptr_t cookie) +static struct binder_node *binder_get_node(struct binder_proc *proc, + binder_uintptr_t ptr) +{ + struct binder_node *node; + + binder_inner_proc_lock(proc); + node = binder_get_node_ilocked(proc, ptr); + binder_inner_proc_unlock(proc); + return node; +} + +static struct binder_node *binder_init_node_ilocked( + struct binder_proc *proc, + struct binder_node *new_node, + struct flat_binder_object *fp) { struct rb_node **p = &proc->nodes.rb_node; struct rb_node *parent = NULL; struct binder_node *node; + binder_uintptr_t ptr = fp ? fp->binder : 0; + binder_uintptr_t cookie = fp ? fp->cookie : 0; + __u32 flags = fp ? fp->flags : 0; + s8 priority; + + assert_spin_locked(&proc->inner_lock); while (*p) { + parent = *p; node = rb_entry(parent, struct binder_node, rb_node); @@ -1018,14 +1281,19 @@ static struct binder_node *binder_new_node(struct binder_proc *proc, p = &(*p)->rb_left; else if (ptr > node->ptr) p = &(*p)->rb_right; - else - return NULL; + else { + /* + * A matching node is already in + * the rb tree. Abandon the init + * and return it. + */ + binder_inc_node_tmpref_ilocked(node); + return node; + } } - - node = kzalloc_preempt_disabled(sizeof(*node)); - if (node == NULL) - return NULL; + node = new_node; binder_stats_created(BINDER_STAT_NODE); + node->tmp_refs++; rb_link_node(&node->rb_node, parent, p); rb_insert_color(&node->rb_node, &proc->nodes); node->debug_id = atomic_inc_return(&binder_last_id); @@ -1033,18 +1301,58 @@ static struct binder_node *binder_new_node(struct binder_proc *proc, node->ptr = ptr; node->cookie = cookie; node->work.type = BINDER_WORK_NODE; + priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; + node->sched_policy = (flags & FLAT_BINDER_FLAG_PRIORITY_MASK) >> + FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; + node->min_priority = to_kernel_prio(node->sched_policy, priority); + node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); + node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT); + spin_lock_init(&node->lock); INIT_LIST_HEAD(&node->work.entry); INIT_LIST_HEAD(&node->async_todo); binder_debug(BINDER_DEBUG_INTERNAL_REFS, "%d:%d node %d u%016llx c%016llx created\n", proc->pid, current->pid, node->debug_id, (u64)node->ptr, (u64)node->cookie); + return node; } -static int binder_inc_node(struct binder_node *node, int strong, int internal, - struct list_head *target_list) +static struct binder_node *binder_new_node(struct binder_proc *proc, + struct flat_binder_object *fp) +{ + struct binder_node *node; + struct binder_node *new_node = kzalloc(sizeof(*node), GFP_KERNEL); + + if (!new_node) + return NULL; + binder_inner_proc_lock(proc); + node = binder_init_node_ilocked(proc, new_node, fp); + binder_inner_proc_unlock(proc); + if (node != new_node) + /* + * The node was already added by another thread + */ + kfree(new_node); + + return node; +} + +static void binder_free_node(struct binder_node *node) +{ + kfree(node); + binder_stats_deleted(BINDER_STAT_NODE); +} + +static int binder_inc_node_nilocked(struct binder_node *node, int strong, + int internal, + struct list_head *target_list) { + struct binder_proc *proc = node->proc; + + assert_spin_locked(&node->lock); + if (proc) + assert_spin_locked(&proc->inner_lock); if (strong) { if (internal) { if (target_list == NULL && @@ -1061,8 +1369,8 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, } else node->local_strong_refs++; if (!node->has_strong_ref && target_list) { - list_del_init(&node->work.entry); - list_add_tail(&node->work.entry, target_list); + binder_dequeue_work_ilocked(&node->work); + binder_enqueue_work_ilocked(&node->work, target_list); } } else { if (!internal) @@ -1073,58 +1381,169 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, node->debug_id); return -EINVAL; } - list_add_tail(&node->work.entry, target_list); + binder_enqueue_work_ilocked(&node->work, target_list); } } return 0; } -static int binder_dec_node(struct binder_node *node, int strong, int internal) +static int binder_inc_node(struct binder_node *node, int strong, int internal, + struct list_head *target_list) +{ + int ret; + + binder_node_inner_lock(node); + ret = binder_inc_node_nilocked(node, strong, internal, target_list); + binder_node_inner_unlock(node); + + return ret; +} + +static bool binder_dec_node_nilocked(struct binder_node *node, + int strong, int internal) { + struct binder_proc *proc = node->proc; + + assert_spin_locked(&node->lock); + if (proc) + assert_spin_locked(&proc->inner_lock); if (strong) { if (internal) node->internal_strong_refs--; else node->local_strong_refs--; if (node->local_strong_refs || node->internal_strong_refs) - return 0; + return false; } else { if (!internal) node->local_weak_refs--; - if (node->local_weak_refs || !hlist_empty(&node->refs)) - return 0; + if (node->local_weak_refs || node->tmp_refs || + !hlist_empty(&node->refs)) + return false; } - if (node->proc && (node->has_strong_ref || node->has_weak_ref)) { + + if (proc && (node->has_strong_ref || node->has_weak_ref)) { if (list_empty(&node->work.entry)) { - list_add_tail(&node->work.entry, &node->proc->todo); - wake_up_interruptible(&node->proc->wait); + binder_enqueue_work_ilocked(&node->work, &proc->todo); + binder_wakeup_proc_ilocked(proc); } } else { if (hlist_empty(&node->refs) && !node->local_strong_refs && - !node->local_weak_refs) { - list_del_init(&node->work.entry); - if (node->proc) { - rb_erase(&node->rb_node, &node->proc->nodes); + !node->local_weak_refs && !node->tmp_refs) { + if (proc) { + binder_dequeue_work_ilocked(&node->work); + rb_erase(&node->rb_node, &proc->nodes); binder_debug(BINDER_DEBUG_INTERNAL_REFS, "refless node %d deleted\n", node->debug_id); } else { + BUG_ON(!list_empty(&node->work.entry)); + spin_lock(&binder_dead_nodes_lock); + /* + * tmp_refs could have changed so + * check it again + */ + if (node->tmp_refs) { + spin_unlock(&binder_dead_nodes_lock); + return false; + } hlist_del(&node->dead_node); + spin_unlock(&binder_dead_nodes_lock); binder_debug(BINDER_DEBUG_INTERNAL_REFS, "dead node %d deleted\n", node->debug_id); } - kfree(node); - binder_stats_deleted(BINDER_STAT_NODE); + return true; } } + return false; +} - return 0; +static void binder_dec_node(struct binder_node *node, int strong, int internal) +{ + bool free_node; + + binder_node_inner_lock(node); + free_node = binder_dec_node_nilocked(node, strong, internal); + binder_node_inner_unlock(node); + if (free_node) + binder_free_node(node); +} + +static void binder_inc_node_tmpref_ilocked(struct binder_node *node) +{ + /* + * No call to binder_inc_node() is needed since we + * don't need to inform userspace of any changes to + * tmp_refs + */ + node->tmp_refs++; } +/** + * binder_inc_node_tmpref() - take a temporary reference on node + * @node: node to reference + * + * Take reference on node to prevent the node from being freed + * while referenced only by a local variable. The inner lock is + * needed to serialize with the node work on the queue (which + * isn't needed after the node is dead). If the node is dead + * (node->proc is NULL), use binder_dead_nodes_lock to protect + * node->tmp_refs against dead-node-only cases where the node + * lock cannot be acquired (eg traversing the dead node list to + * print nodes) + */ +static void binder_inc_node_tmpref(struct binder_node *node) +{ + binder_node_lock(node); + if (node->proc) + binder_inner_proc_lock(node->proc); + else + spin_lock(&binder_dead_nodes_lock); + binder_inc_node_tmpref_ilocked(node); + if (node->proc) + binder_inner_proc_unlock(node->proc); + else + spin_unlock(&binder_dead_nodes_lock); + binder_node_unlock(node); +} + +/** + * binder_dec_node_tmpref() - remove a temporary reference on node + * @node: node to reference + * + * Release temporary reference on node taken via binder_inc_node_tmpref() + */ +static void binder_dec_node_tmpref(struct binder_node *node) +{ + bool free_node; + + binder_node_inner_lock(node); + if (!node->proc) + spin_lock(&binder_dead_nodes_lock); + node->tmp_refs--; + BUG_ON(node->tmp_refs < 0); + if (!node->proc) + spin_unlock(&binder_dead_nodes_lock); + /* + * Call binder_dec_node() to check if all refcounts are 0 + * and cleanup is needed. Calling with strong=0 and internal=1 + * causes no actual reference to be released in binder_dec_node(). + * If that changes, a change is needed here too. + */ + free_node = binder_dec_node_nilocked(node, 0, 1); + binder_node_inner_unlock(node); + if (free_node) + binder_free_node(node); +} -static struct binder_ref *binder_get_ref(struct binder_proc *proc, - u32 desc, bool need_strong_ref) +static void binder_put_node(struct binder_node *node) +{ + binder_dec_node_tmpref(node); +} + +static struct binder_ref *binder_get_ref_olocked(struct binder_proc *proc, + u32 desc, bool need_strong_ref) { struct rb_node *n = proc->refs_by_desc.rb_node; struct binder_ref *ref; @@ -1132,11 +1551,11 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc, while (n) { ref = rb_entry(n, struct binder_ref, rb_node_desc); - if (desc < ref->desc) { + if (desc < ref->data.desc) { n = n->rb_left; - } else if (desc > ref->desc) { + } else if (desc > ref->data.desc) { n = n->rb_right; - } else if (need_strong_ref && !ref->strong) { + } else if (need_strong_ref && !ref->data.strong) { binder_user_error("tried to use weak ref as strong ref\n"); return NULL; } else { @@ -1146,14 +1565,34 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc, return NULL; } -static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, - struct binder_node *node) +/** + * binder_get_ref_for_node_olocked() - get the ref associated with given node + * @proc: binder_proc that owns the ref + * @node: binder_node of target + * @new_ref: newly allocated binder_ref to be initialized or %NULL + * + * Look up the ref for the given node and return it if it exists + * + * If it doesn't exist and the caller provides a newly allocated + * ref, initialize the fields of the newly allocated ref and insert + * into the given proc rb_trees and node refs list. + * + * Return: the ref for node. It is possible that another thread + * allocated/initialized the ref first in which case the + * returned ref would be different than the passed-in + * new_ref. new_ref must be kfree'd by the caller in + * this case. + */ +static struct binder_ref *binder_get_ref_for_node_olocked( + struct binder_proc *proc, + struct binder_node *node, + struct binder_ref *new_ref) { - struct rb_node *n; + struct binder_context *context = proc->context; struct rb_node **p = &proc->refs_by_node.rb_node; struct rb_node *parent = NULL; - struct binder_ref *ref, *new_ref; - struct binder_context *context = proc->context; + struct binder_ref *ref; + struct rb_node *n; while (*p) { parent = *p; @@ -1166,22 +1605,22 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, else return ref; } - new_ref = kzalloc_preempt_disabled(sizeof(*ref)); - if (new_ref == NULL) + if (!new_ref) return NULL; + binder_stats_created(BINDER_STAT_REF); - new_ref->debug_id = atomic_inc_return(&binder_last_id); + new_ref->data.debug_id = atomic_inc_return(&binder_last_id); new_ref->proc = proc; new_ref->node = node; rb_link_node(&new_ref->rb_node_node, parent, p); rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); - new_ref->desc = (node == context->binder_context_mgr_node) ? 0 : 1; + new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1; for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { ref = rb_entry(n, struct binder_ref, rb_node_desc); - if (ref->desc > new_ref->desc) + if (ref->data.desc > new_ref->data.desc) break; - new_ref->desc = ref->desc + 1; + new_ref->data.desc = ref->data.desc + 1; } p = &proc->refs_by_desc.rb_node; @@ -1189,121 +1628,423 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, parent = *p; ref = rb_entry(parent, struct binder_ref, rb_node_desc); - if (new_ref->desc < ref->desc) + if (new_ref->data.desc < ref->data.desc) p = &(*p)->rb_left; - else if (new_ref->desc > ref->desc) + else if (new_ref->data.desc > ref->data.desc) p = &(*p)->rb_right; else BUG(); } rb_link_node(&new_ref->rb_node_desc, parent, p); rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc); - if (node) { - hlist_add_head(&new_ref->node_entry, &node->refs); - binder_debug(BINDER_DEBUG_INTERNAL_REFS, - "%d new ref %d desc %d for node %d\n", - proc->pid, new_ref->debug_id, new_ref->desc, - node->debug_id); - } else { - binder_debug(BINDER_DEBUG_INTERNAL_REFS, - "%d new ref %d desc %d for dead node\n", - proc->pid, new_ref->debug_id, new_ref->desc); - } + binder_node_lock(node); + hlist_add_head(&new_ref->node_entry, &node->refs); + + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "%d new ref %d desc %d for node %d\n", + proc->pid, new_ref->data.debug_id, new_ref->data.desc, + node->debug_id); + binder_node_unlock(node); return new_ref; } -static void binder_delete_ref(struct binder_ref *ref) +static void binder_cleanup_ref_olocked(struct binder_ref *ref) { + bool delete_node = false; + binder_debug(BINDER_DEBUG_INTERNAL_REFS, "%d delete ref %d desc %d for node %d\n", - ref->proc->pid, ref->debug_id, ref->desc, + ref->proc->pid, ref->data.debug_id, ref->data.desc, ref->node->debug_id); rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc); rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node); - if (ref->strong) - binder_dec_node(ref->node, 1, 1); + + binder_node_inner_lock(ref->node); + if (ref->data.strong) + binder_dec_node_nilocked(ref->node, 1, 1); + hlist_del(&ref->node_entry); - binder_dec_node(ref->node, 0, 1); + delete_node = binder_dec_node_nilocked(ref->node, 0, 1); + binder_node_inner_unlock(ref->node); + /* + * Clear ref->node unless we want the caller to free the node + */ + if (!delete_node) { + /* + * The caller uses ref->node to determine + * whether the node needs to be freed. Clear + * it since the node is still alive. + */ + ref->node = NULL; + } + if (ref->death) { binder_debug(BINDER_DEBUG_DEAD_BINDER, "%d delete ref %d desc %d has death notification\n", - ref->proc->pid, ref->debug_id, ref->desc); - list_del(&ref->death->work.entry); - kfree(ref->death); + ref->proc->pid, ref->data.debug_id, + ref->data.desc); + binder_dequeue_work(ref->proc, &ref->death->work); binder_stats_deleted(BINDER_STAT_DEATH); } - kfree(ref); binder_stats_deleted(BINDER_STAT_REF); } -static int binder_inc_ref(struct binder_ref *ref, int strong, - struct list_head *target_list) +/** + * binder_inc_ref_olocked() - increment the ref for given handle + * @ref: ref to be incremented + * @strong: if true, strong increment, else weak + * @target_list: list to queue node work on + * + * Increment the ref. @ref->proc->outer_lock must be held on entry + * + * Return: 0, if successful, else errno + */ +static int binder_inc_ref_olocked(struct binder_ref *ref, int strong, + struct list_head *target_list) { int ret; if (strong) { - if (ref->strong == 0) { + if (ref->data.strong == 0) { ret = binder_inc_node(ref->node, 1, 1, target_list); if (ret) return ret; } - ref->strong++; + ref->data.strong++; } else { - if (ref->weak == 0) { + if (ref->data.weak == 0) { ret = binder_inc_node(ref->node, 0, 1, target_list); if (ret) return ret; } - ref->weak++; + ref->data.weak++; } return 0; } - -static int binder_dec_ref(struct binder_ref *ref, int strong) +/** + * binder_dec_ref() - dec the ref for given handle + * @ref: ref to be decremented + * @strong: if true, strong decrement, else weak + * + * Decrement the ref. + * + * Return: true if ref is cleaned up and ready to be freed + */ +static bool binder_dec_ref_olocked(struct binder_ref *ref, int strong) { if (strong) { - if (ref->strong == 0) { + if (ref->data.strong == 0) { binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n", - ref->proc->pid, ref->debug_id, - ref->desc, ref->strong, ref->weak); - return -EINVAL; - } - ref->strong--; - if (ref->strong == 0) { - int ret; - - ret = binder_dec_node(ref->node, strong, 1); - if (ret) - return ret; + ref->proc->pid, ref->data.debug_id, + ref->data.desc, ref->data.strong, + ref->data.weak); + return false; } + ref->data.strong--; + if (ref->data.strong == 0) + binder_dec_node(ref->node, strong, 1); } else { - if (ref->weak == 0) { + if (ref->data.weak == 0) { binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n", - ref->proc->pid, ref->debug_id, - ref->desc, ref->strong, ref->weak); - return -EINVAL; + ref->proc->pid, ref->data.debug_id, + ref->data.desc, ref->data.strong, + ref->data.weak); + return false; } - ref->weak--; + ref->data.weak--; } - if (ref->strong == 0 && ref->weak == 0) - binder_delete_ref(ref); - return 0; + if (ref->data.strong == 0 && ref->data.weak == 0) { + binder_cleanup_ref_olocked(ref); + return true; + } + return false; } -static void binder_pop_transaction(struct binder_thread *target_thread, - struct binder_transaction *t) +/** + * binder_get_node_from_ref() - get the node from the given proc/desc + * @proc: proc containing the ref + * @desc: the handle associated with the ref + * @need_strong_ref: if true, only return node if ref is strong + * @rdata: the id/refcount data for the ref + * + * Given a proc and ref handle, return the associated binder_node + * + * Return: a binder_node or NULL if not found or not strong when strong required + */ +static struct binder_node *binder_get_node_from_ref( + struct binder_proc *proc, + u32 desc, bool need_strong_ref, + struct binder_ref_data *rdata) { - if (target_thread) { - BUG_ON(target_thread->transaction_stack != t); - BUG_ON(target_thread->transaction_stack->from != target_thread); - target_thread->transaction_stack = - target_thread->transaction_stack->from_parent; - t->from = NULL; + struct binder_node *node; + struct binder_ref *ref; + + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, desc, need_strong_ref); + if (!ref) + goto err_no_ref; + node = ref->node; + /* + * Take an implicit reference on the node to ensure + * it stays alive until the call to binder_put_node() + */ + binder_inc_node_tmpref(node); + if (rdata) + *rdata = ref->data; + binder_proc_unlock(proc); + + return node; + +err_no_ref: + binder_proc_unlock(proc); + return NULL; +} + +/** + * binder_free_ref() - free the binder_ref + * @ref: ref to free + * + * Free the binder_ref. Free the binder_node indicated by ref->node + * (if non-NULL) and the binder_ref_death indicated by ref->death. + */ +static void binder_free_ref(struct binder_ref *ref) +{ + if (ref->node) + binder_free_node(ref->node); + kfree(ref->death); + kfree(ref); +} + +/** + * binder_update_ref_for_handle() - inc/dec the ref for given handle + * @proc: proc containing the ref + * @desc: the handle associated with the ref + * @increment: true=inc reference, false=dec reference + * @strong: true=strong reference, false=weak reference + * @rdata: the id/refcount data for the ref + * + * Given a proc and ref handle, increment or decrement the ref + * according to "increment" arg. + * + * Return: 0 if successful, else errno + */ +static int binder_update_ref_for_handle(struct binder_proc *proc, + uint32_t desc, bool increment, bool strong, + struct binder_ref_data *rdata) +{ + int ret = 0; + struct binder_ref *ref; + bool delete_ref = false; + + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, desc, strong); + if (!ref) { + ret = -EINVAL; + goto err_no_ref; + } + if (increment) + ret = binder_inc_ref_olocked(ref, strong, NULL); + else + delete_ref = binder_dec_ref_olocked(ref, strong); + + if (rdata) + *rdata = ref->data; + binder_proc_unlock(proc); + + if (delete_ref) + binder_free_ref(ref); + return ret; + +err_no_ref: + binder_proc_unlock(proc); + return ret; +} + +/** + * binder_dec_ref_for_handle() - dec the ref for given handle + * @proc: proc containing the ref + * @desc: the handle associated with the ref + * @strong: true=strong reference, false=weak reference + * @rdata: the id/refcount data for the ref + * + * Just calls binder_update_ref_for_handle() to decrement the ref. + * + * Return: 0 if successful, else errno + */ +static int binder_dec_ref_for_handle(struct binder_proc *proc, + uint32_t desc, bool strong, struct binder_ref_data *rdata) +{ + return binder_update_ref_for_handle(proc, desc, false, strong, rdata); +} + + +/** + * binder_inc_ref_for_node() - increment the ref for given proc/node + * @proc: proc containing the ref + * @node: target node + * @strong: true=strong reference, false=weak reference + * @target_list: worklist to use if node is incremented + * @rdata: the id/refcount data for the ref + * + * Given a proc and node, increment the ref. Create the ref if it + * doesn't already exist + * + * Return: 0 if successful, else errno + */ +static int binder_inc_ref_for_node(struct binder_proc *proc, + struct binder_node *node, + bool strong, + struct list_head *target_list, + struct binder_ref_data *rdata) +{ + struct binder_ref *ref; + struct binder_ref *new_ref = NULL; + int ret = 0; + + binder_proc_lock(proc); + ref = binder_get_ref_for_node_olocked(proc, node, NULL); + if (!ref) { + binder_proc_unlock(proc); + new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); + if (!new_ref) + return -ENOMEM; + binder_proc_lock(proc); + ref = binder_get_ref_for_node_olocked(proc, node, new_ref); + } + ret = binder_inc_ref_olocked(ref, strong, target_list); + *rdata = ref->data; + binder_proc_unlock(proc); + if (new_ref && ref != new_ref) + /* + * Another thread created the ref first so + * free the one we allocated + */ + kfree(new_ref); + return ret; +} + +static void binder_pop_transaction_ilocked(struct binder_thread *target_thread, + struct binder_transaction *t) +{ + BUG_ON(!target_thread); + assert_spin_locked(&target_thread->proc->inner_lock); + BUG_ON(target_thread->transaction_stack != t); + BUG_ON(target_thread->transaction_stack->from != target_thread); + target_thread->transaction_stack = + target_thread->transaction_stack->from_parent; + t->from = NULL; +} + +/** + * binder_thread_dec_tmpref() - decrement thread->tmp_ref + * @thread: thread to decrement + * + * A thread needs to be kept alive while being used to create or + * handle a transaction. binder_get_txn_from() is used to safely + * extract t->from from a binder_transaction and keep the thread + * indicated by t->from from being freed. When done with that + * binder_thread, this function is called to decrement the + * tmp_ref and free if appropriate (thread has been released + * and no transaction being processed by the driver) + */ +static void binder_thread_dec_tmpref(struct binder_thread *thread) +{ + /* + * atomic is used to protect the counter value while + * it cannot reach zero or thread->is_dead is false + */ + binder_inner_proc_lock(thread->proc); + atomic_dec(&thread->tmp_ref); + if (thread->is_dead && !atomic_read(&thread->tmp_ref)) { + binder_inner_proc_unlock(thread->proc); + binder_free_thread(thread); + return; } - t->need_reply = 0; + binder_inner_proc_unlock(thread->proc); +} + +/** + * binder_proc_dec_tmpref() - decrement proc->tmp_ref + * @proc: proc to decrement + * + * A binder_proc needs to be kept alive while being used to create or + * handle a transaction. proc->tmp_ref is incremented when + * creating a new transaction or the binder_proc is currently in-use + * by threads that are being released. When done with the binder_proc, + * this function is called to decrement the counter and free the + * proc if appropriate (proc has been released, all threads have + * been released and not currenly in-use to process a transaction). + */ +static void binder_proc_dec_tmpref(struct binder_proc *proc) +{ + binder_inner_proc_lock(proc); + proc->tmp_ref--; + if (proc->is_dead && RB_EMPTY_ROOT(&proc->threads) && + !proc->tmp_ref) { + binder_inner_proc_unlock(proc); + binder_free_proc(proc); + return; + } + binder_inner_proc_unlock(proc); +} + +/** + * binder_get_txn_from() - safely extract the "from" thread in transaction + * @t: binder transaction for t->from + * + * Atomically return the "from" thread and increment the tmp_ref + * count for the thread to ensure it stays alive until + * binder_thread_dec_tmpref() is called. + * + * Return: the value of t->from + */ +static struct binder_thread *binder_get_txn_from( + struct binder_transaction *t) +{ + struct binder_thread *from; + + spin_lock(&t->lock); + from = t->from; + if (from) + atomic_inc(&from->tmp_ref); + spin_unlock(&t->lock); + return from; +} + +/** + * binder_get_txn_from_and_acq_inner() - get t->from and acquire inner lock + * @t: binder transaction for t->from + * + * Same as binder_get_txn_from() except it also acquires the proc->inner_lock + * to guarantee that the thread cannot be released while operating on it. + * The caller must call binder_inner_proc_unlock() to release the inner lock + * as well as call binder_dec_thread_txn() to release the reference. + * + * Return: the value of t->from + */ +static struct binder_thread *binder_get_txn_from_and_acq_inner( + struct binder_transaction *t) +{ + struct binder_thread *from; + + from = binder_get_txn_from(t); + if (!from) + return NULL; + binder_inner_proc_lock(from->proc); + if (t->from) { + BUG_ON(from != t->from); + return from; + } + binder_inner_proc_unlock(from->proc); + binder_thread_dec_tmpref(from); + return NULL; +} + +static void binder_free_transaction(struct binder_transaction *t) +{ if (t->buffer) t->buffer->transaction = NULL; kfree(t); @@ -1318,30 +2059,28 @@ static void binder_send_failed_reply(struct binder_transaction *t, BUG_ON(t->flags & TF_ONE_WAY); while (1) { - target_thread = t->from; + target_thread = binder_get_txn_from_and_acq_inner(t); if (target_thread) { - if (target_thread->return_error != BR_OK && - target_thread->return_error2 == BR_OK) { - target_thread->return_error2 = - target_thread->return_error; - target_thread->return_error = BR_OK; - } - if (target_thread->return_error == BR_OK) { - binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, - "send failed reply for transaction %d to %d:%d\n", - t->debug_id, - target_thread->proc->pid, - target_thread->pid); - - binder_pop_transaction(target_thread, t); - target_thread->return_error = error_code; + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, + "send failed reply for transaction %d to %d:%d\n", + t->debug_id, + target_thread->proc->pid, + target_thread->pid); + + binder_pop_transaction_ilocked(target_thread, t); + if (target_thread->reply_error.cmd == BR_OK) { + target_thread->reply_error.cmd = error_code; + binder_enqueue_work_ilocked( + &target_thread->reply_error.work, + &target_thread->todo); wake_up_interruptible(&target_thread->wait); } else { - pr_err("reply failed, target thread, %d:%d, has error code %d already\n", - target_thread->proc->pid, - target_thread->pid, - target_thread->return_error); + WARN(1, "Unexpected reply error: %u\n", + target_thread->reply_error.cmd); } + binder_inner_proc_unlock(target_thread->proc); + binder_thread_dec_tmpref(target_thread); + binder_free_transaction(t); return; } next = t->from_parent; @@ -1350,7 +2089,7 @@ static void binder_send_failed_reply(struct binder_transaction *t, "send failed reply for transaction %d, target dead\n", t->debug_id); - binder_pop_transaction(target_thread, t); + binder_free_transaction(t); if (next == NULL) { binder_debug(BINDER_DEBUG_DEAD_BINDER, "reply failed, no target thread at root\n"); @@ -1559,25 +2298,26 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, node->debug_id, (u64)node->ptr); binder_dec_node(node, hdr->type == BINDER_TYPE_BINDER, 0); + binder_put_node(node); } break; case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: { struct flat_binder_object *fp; - struct binder_ref *ref; + struct binder_ref_data rdata; + int ret; fp = to_flat_binder_object(hdr); - ref = binder_get_ref(proc, fp->handle, - hdr->type == BINDER_TYPE_HANDLE); + ret = binder_dec_ref_for_handle(proc, fp->handle, + hdr->type == BINDER_TYPE_HANDLE, &rdata); - if (ref == NULL) { - pr_err("transaction release %d bad handle %d\n", - debug_id, fp->handle); + if (ret) { + pr_err("transaction release %d bad handle %d, ret = %d\n", + debug_id, fp->handle, ret); break; } binder_debug(BINDER_DEBUG_TRANSACTION, - " ref %d desc %d (node %d)\n", - ref->debug_id, ref->desc, ref->node->debug_id); - binder_dec_ref(ref, hdr->type == BINDER_TYPE_HANDLE); + " ref %d desc %d\n", + rdata.debug_id, rdata.desc); } break; case BINDER_TYPE_FD: { @@ -1616,7 +2356,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, * back to kernel address space to access it */ parent_buffer = parent->buffer - - proc->user_buffer_offset; + binder_alloc_get_user_buffer_offset( + &proc->alloc); fd_buf_size = sizeof(u32) * fda->num_fds; if (fda->num_fds >= SIZE_MAX / sizeof(u32)) { @@ -1648,102 +2389,122 @@ static int binder_translate_binder(struct flat_binder_object *fp, struct binder_thread *thread) { struct binder_node *node; - struct binder_ref *ref; struct binder_proc *proc = thread->proc; struct binder_proc *target_proc = t->to_proc; + struct binder_ref_data rdata; + int ret = 0; node = binder_get_node(proc, fp->binder); if (!node) { - node = binder_new_node(proc, fp->binder, fp->cookie); + node = binder_new_node(proc, fp); if (!node) return -ENOMEM; - - node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; - node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); } if (fp->cookie != node->cookie) { binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n", proc->pid, thread->pid, (u64)fp->binder, node->debug_id, (u64)fp->cookie, (u64)node->cookie); - return -EINVAL; + ret = -EINVAL; + goto done; + } + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + ret = -EPERM; + goto done; } - if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) - return -EPERM; - ref = binder_get_ref_for_node(target_proc, node); - if (!ref) - return -EINVAL; + ret = binder_inc_ref_for_node(target_proc, node, + fp->hdr.type == BINDER_TYPE_BINDER, + &thread->todo, &rdata); + if (ret) + goto done; if (fp->hdr.type == BINDER_TYPE_BINDER) fp->hdr.type = BINDER_TYPE_HANDLE; else fp->hdr.type = BINDER_TYPE_WEAK_HANDLE; fp->binder = 0; - fp->handle = ref->desc; + fp->handle = rdata.desc; fp->cookie = 0; - binder_inc_ref(ref, fp->hdr.type == BINDER_TYPE_HANDLE, &thread->todo); - trace_binder_transaction_node_to_ref(t, node, ref); + trace_binder_transaction_node_to_ref(t, node, &rdata); binder_debug(BINDER_DEBUG_TRANSACTION, " node %d u%016llx -> ref %d desc %d\n", node->debug_id, (u64)node->ptr, - ref->debug_id, ref->desc); - - return 0; + rdata.debug_id, rdata.desc); +done: + binder_put_node(node); + return ret; } static int binder_translate_handle(struct flat_binder_object *fp, struct binder_transaction *t, struct binder_thread *thread) { - struct binder_ref *ref; struct binder_proc *proc = thread->proc; struct binder_proc *target_proc = t->to_proc; + struct binder_node *node; + struct binder_ref_data src_rdata; + int ret = 0; - ref = binder_get_ref(proc, fp->handle, - fp->hdr.type == BINDER_TYPE_HANDLE); - if (!ref) { + node = binder_get_node_from_ref(proc, fp->handle, + fp->hdr.type == BINDER_TYPE_HANDLE, &src_rdata); + if (!node) { binder_user_error("%d:%d got transaction with invalid handle, %d\n", proc->pid, thread->pid, fp->handle); return -EINVAL; } - if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) - return -EPERM; + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + ret = -EPERM; + goto done; + } - if (ref->node->proc == target_proc) { + binder_node_lock(node); + if (node->proc == target_proc) { if (fp->hdr.type == BINDER_TYPE_HANDLE) fp->hdr.type = BINDER_TYPE_BINDER; else fp->hdr.type = BINDER_TYPE_WEAK_BINDER; - fp->binder = ref->node->ptr; - fp->cookie = ref->node->cookie; - binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER, - 0, NULL); - trace_binder_transaction_ref_to_node(t, ref); + fp->binder = node->ptr; + fp->cookie = node->cookie; + if (node->proc) + binder_inner_proc_lock(node->proc); + binder_inc_node_nilocked(node, + fp->hdr.type == BINDER_TYPE_BINDER, + 0, NULL); + if (node->proc) + binder_inner_proc_unlock(node->proc); + trace_binder_transaction_ref_to_node(t, node, &src_rdata); binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d -> node %d u%016llx\n", - ref->debug_id, ref->desc, ref->node->debug_id, - (u64)ref->node->ptr); + src_rdata.debug_id, src_rdata.desc, node->debug_id, + (u64)node->ptr); + binder_node_unlock(node); } else { - struct binder_ref *new_ref; + int ret; + struct binder_ref_data dest_rdata; - new_ref = binder_get_ref_for_node(target_proc, ref->node); - if (!new_ref) - return -EINVAL; + binder_node_unlock(node); + ret = binder_inc_ref_for_node(target_proc, node, + fp->hdr.type == BINDER_TYPE_HANDLE, + NULL, &dest_rdata); + if (ret) + goto done; fp->binder = 0; - fp->handle = new_ref->desc; + fp->handle = dest_rdata.desc; fp->cookie = 0; - binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE, - NULL); - trace_binder_transaction_ref_to_ref(t, ref, new_ref); + trace_binder_transaction_ref_to_ref(t, node, &src_rdata, + &dest_rdata); binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d -> ref %d desc %d (node %d)\n", - ref->debug_id, ref->desc, new_ref->debug_id, - new_ref->desc, ref->node->debug_id); + src_rdata.debug_id, src_rdata.desc, + dest_rdata.debug_id, dest_rdata.desc, + node->debug_id); } - return 0; +done: + binder_put_node(node); + return ret; } static int binder_translate_fd(int fd, @@ -1834,7 +2595,8 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, * Since the parent was already fixed up, convert it * back to the kernel address space to access it */ - parent_buffer = parent->buffer - target_proc->user_buffer_offset; + parent_buffer = parent->buffer - + binder_alloc_get_user_buffer_offset(&target_proc->alloc); fd_array = (u32 *)(parent_buffer + fda->parent_offset); if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) { binder_user_error("%d:%d parent offset not aligned correctly.\n", @@ -1902,12 +2664,87 @@ static int binder_fixup_parent(struct binder_transaction *t, return -EINVAL; } parent_buffer = (u8 *)(parent->buffer - - target_proc->user_buffer_offset); + binder_alloc_get_user_buffer_offset( + &target_proc->alloc)); *(binder_uintptr_t *)(parent_buffer + bp->parent_offset) = bp->buffer; return 0; } +/** + * binder_proc_transaction() - sends a transaction to a process and wakes it up + * @t: transaction to send + * @proc: process to send the transaction to + * @thread: thread in @proc to send the transaction to (may be NULL) + * + * This function queues a transaction to the specified process. It will try + * to find a thread in the target process to handle the transaction and + * wake it up. If no thread is found, the work is queued to the proc + * waitqueue. + * + * If the @thread parameter is not NULL, the transaction is always queued + * to the waitlist of that specific thread. + * + * Return: true if the transactions was successfully queued + * false if the target process or thread is dead + */ +static bool binder_proc_transaction(struct binder_transaction *t, + struct binder_proc *proc, + struct binder_thread *thread) +{ + struct list_head *target_list = NULL; + struct binder_node *node = t->buffer->target_node; + struct binder_priority node_prio; + bool oneway = !!(t->flags & TF_ONE_WAY); + bool wakeup = true; + + BUG_ON(!node); + binder_node_lock(node); + node_prio.prio = node->min_priority; + node_prio.sched_policy = node->sched_policy; + + if (oneway) { + BUG_ON(thread); + if (node->has_async_transaction) { + target_list = &node->async_todo; + wakeup = false; + } else { + node->has_async_transaction = 1; + } + } + + binder_inner_proc_lock(proc); + + if (proc->is_dead || (thread && thread->is_dead)) { + binder_inner_proc_unlock(proc); + binder_node_unlock(node); + return false; + } + + if (!thread && !target_list) + thread = binder_select_thread_ilocked(proc); + + if (thread) { + target_list = &thread->todo; + binder_transaction_priority(thread->task, t, node_prio, + node->inherit_rt); + } else if (!target_list) { + target_list = &proc->todo; + } else { + BUG_ON(target_list != &node->async_todo); + } + + binder_enqueue_work_ilocked(&t->work, target_list); + + if (wakeup) + binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); + + binder_inner_proc_unlock(proc); + binder_node_unlock(node); + + return true; +} + static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply, @@ -1919,19 +2756,21 @@ static void binder_transaction(struct binder_proc *proc, binder_size_t *offp, *off_end, *off_start; binder_size_t off_min; u8 *sg_bufp, *sg_buf_end; - struct binder_proc *target_proc; + struct binder_proc *target_proc = NULL; struct binder_thread *target_thread = NULL; struct binder_node *target_node = NULL; - struct list_head *target_list; - wait_queue_head_t *target_wait; struct binder_transaction *in_reply_to = NULL; struct binder_transaction_log_entry *e; - uint32_t return_error; + uint32_t return_error = 0; + uint32_t return_error_param = 0; + uint32_t return_error_line = 0; struct binder_buffer_object *last_fixup_obj = NULL; binder_size_t last_fixup_min_off = 0; struct binder_context *context = proc->context; + int t_debug_id = atomic_inc_return(&binder_last_id); - e = binder_transaction_log_add(&context->transaction_log); + e = binder_transaction_log_add(&binder_transaction_log); + e->debug_id = t_debug_id; e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY); e->from_proc = proc->pid; e->from_thread = thread->pid; @@ -1941,29 +2780,39 @@ static void binder_transaction(struct binder_proc *proc, e->context_name = proc->context->name; if (reply) { + binder_inner_proc_lock(proc); in_reply_to = thread->transaction_stack; if (in_reply_to == NULL) { + binder_inner_proc_unlock(proc); binder_user_error("%d:%d got reply transaction with no transaction stack\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EPROTO; + return_error_line = __LINE__; goto err_empty_call_stack; } - binder_set_nice(in_reply_to->saved_priority); if (in_reply_to->to_thread != thread) { + spin_lock(&in_reply_to->lock); binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n", proc->pid, thread->pid, in_reply_to->debug_id, in_reply_to->to_proc ? in_reply_to->to_proc->pid : 0, in_reply_to->to_thread ? in_reply_to->to_thread->pid : 0); + spin_unlock(&in_reply_to->lock); + binder_inner_proc_unlock(proc); return_error = BR_FAILED_REPLY; + return_error_param = -EPROTO; + return_error_line = __LINE__; in_reply_to = NULL; goto err_bad_call_stack; } thread->transaction_stack = in_reply_to->to_parent; - target_thread = in_reply_to->from; + binder_inner_proc_unlock(proc); + target_thread = binder_get_txn_from_and_acq_inner(in_reply_to); if (target_thread == NULL) { return_error = BR_DEAD_REPLY; + return_error_line = __LINE__; goto err_dead_binder; } if (target_thread->transaction_stack != in_reply_to) { @@ -1972,89 +2821,137 @@ static void binder_transaction(struct binder_proc *proc, target_thread->transaction_stack ? target_thread->transaction_stack->debug_id : 0, in_reply_to->debug_id); + binder_inner_proc_unlock(target_thread->proc); return_error = BR_FAILED_REPLY; + return_error_param = -EPROTO; + return_error_line = __LINE__; in_reply_to = NULL; target_thread = NULL; goto err_dead_binder; } target_proc = target_thread->proc; + target_proc->tmp_ref++; + binder_inner_proc_unlock(target_thread->proc); } else { if (tr->target.handle) { struct binder_ref *ref; - ref = binder_get_ref(proc, tr->target.handle, true); - if (ref == NULL) { + /* + * There must already be a strong ref + * on this node. If so, do a strong + * increment on the node to ensure it + * stays alive until the transaction is + * done. + */ + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, tr->target.handle, + true); + if (ref) { + binder_inc_node(ref->node, 1, 0, NULL); + target_node = ref->node; + } + binder_proc_unlock(proc); + if (target_node == NULL) { binder_user_error("%d:%d got transaction to invalid handle\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_invalid_target_handle; } - target_node = ref->node; } else { + mutex_lock(&context->context_mgr_node_lock); target_node = context->binder_context_mgr_node; if (target_node == NULL) { return_error = BR_DEAD_REPLY; + mutex_unlock(&context->context_mgr_node_lock); + return_error_line = __LINE__; goto err_no_context_mgr_node; } + binder_inc_node(target_node, 1, 0, NULL); + mutex_unlock(&context->context_mgr_node_lock); } e->to_node = target_node->debug_id; + binder_node_lock(target_node); target_proc = target_node->proc; if (target_proc == NULL) { + binder_node_unlock(target_node); return_error = BR_DEAD_REPLY; + return_error_line = __LINE__; goto err_dead_binder; } + binder_inner_proc_lock(target_proc); + target_proc->tmp_ref++; + binder_inner_proc_unlock(target_proc); + binder_node_unlock(target_node); if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) { return_error = BR_FAILED_REPLY; + return_error_param = -EPERM; + return_error_line = __LINE__; goto err_invalid_target_handle; } + binder_inner_proc_lock(proc); if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { struct binder_transaction *tmp; tmp = thread->transaction_stack; if (tmp->to_thread != thread) { + spin_lock(&tmp->lock); binder_user_error("%d:%d got new transaction with bad transaction stack, transaction %d has target %d:%d\n", proc->pid, thread->pid, tmp->debug_id, tmp->to_proc ? tmp->to_proc->pid : 0, tmp->to_thread ? tmp->to_thread->pid : 0); + spin_unlock(&tmp->lock); + binder_inner_proc_unlock(proc); return_error = BR_FAILED_REPLY; + return_error_param = -EPROTO; + return_error_line = __LINE__; goto err_bad_call_stack; } while (tmp) { - if (tmp->from && tmp->from->proc == target_proc) - target_thread = tmp->from; + struct binder_thread *from; + + spin_lock(&tmp->lock); + from = tmp->from; + if (from && from->proc == target_proc) { + atomic_inc(&from->tmp_ref); + target_thread = from; + spin_unlock(&tmp->lock); + break; + } + spin_unlock(&tmp->lock); tmp = tmp->from_parent; } } + binder_inner_proc_unlock(proc); } - if (target_thread) { + if (target_thread) e->to_thread = target_thread->pid; - target_list = &target_thread->todo; - target_wait = &target_thread->wait; - } else { - target_list = &target_proc->todo; - target_wait = &target_proc->wait; - } e->to_proc = target_proc->pid; /* TODO: reuse incoming transaction for reply */ - t = kzalloc_preempt_disabled(sizeof(*t)); + t = kzalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) { return_error = BR_FAILED_REPLY; + return_error_param = -ENOMEM; + return_error_line = __LINE__; goto err_alloc_t_failed; } binder_stats_created(BINDER_STAT_TRANSACTION); + spin_lock_init(&t->lock); - tcomplete = kzalloc_preempt_disabled(sizeof(*tcomplete)); + tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); if (tcomplete == NULL) { return_error = BR_FAILED_REPLY; + return_error_param = -ENOMEM; + return_error_line = __LINE__; goto err_alloc_tcomplete_failed; } binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE); - t->debug_id = atomic_inc_return(&binder_last_id); - e->debug_id = t->debug_id; + t->debug_id = t_debug_id; if (reply) binder_debug(BINDER_DEBUG_TRANSACTION, @@ -2084,15 +2981,30 @@ static void binder_transaction(struct binder_proc *proc, t->to_thread = target_thread; t->code = tr->code; t->flags = tr->flags; - t->priority = task_nice(current); + if (!(t->flags & TF_ONE_WAY) && + binder_supported_policy(current->policy)) { + /* Inherit supported policies for synchronous transactions */ + t->priority.sched_policy = current->policy; + t->priority.prio = current->normal_prio; + } else { + /* Otherwise, fall back to the default priority */ + t->priority = target_proc->default_priority; + } trace_binder_transaction(reply, t, target_node); - t->buffer = binder_alloc_buf(target_proc, tr->data_size, + t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size, tr->offsets_size, extra_buffers_size, !reply && (t->flags & TF_ONE_WAY)); - if (t->buffer == NULL) { - return_error = BR_FAILED_REPLY; + if (IS_ERR(t->buffer)) { + /* + * -ESRCH indicates VMA cleared. The target is dying. + */ + return_error_param = PTR_ERR(t->buffer); + return_error = return_error_param == -ESRCH ? + BR_DEAD_REPLY : BR_FAILED_REPLY; + return_error_line = __LINE__; + t->buffer = NULL; goto err_binder_alloc_buf_failed; } t->buffer->allow_user_free = 0; @@ -2100,31 +3012,34 @@ static void binder_transaction(struct binder_proc *proc, t->buffer->transaction = t; t->buffer->target_node = target_node; trace_binder_transaction_alloc_buf(t->buffer); - if (target_node) - binder_inc_node(target_node, 1, 0, NULL); - off_start = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); offp = off_start; - if (copy_from_user_preempt_disabled(t->buffer->data, (const void __user *)(uintptr_t) + if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t) tr->data.ptr.buffer, tr->data_size)) { binder_user_error("%d:%d got transaction with invalid data ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EFAULT; + return_error_line = __LINE__; goto err_copy_data_failed; } - if (copy_from_user_preempt_disabled(offp, (const void __user *)(uintptr_t) + if (copy_from_user(offp, (const void __user *)(uintptr_t) tr->data.ptr.offsets, tr->offsets_size)) { binder_user_error("%d:%d got transaction with invalid offsets ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EFAULT; + return_error_line = __LINE__; goto err_copy_data_failed; } if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) { binder_user_error("%d:%d got transaction with invalid offsets size, %lld\n", proc->pid, thread->pid, (u64)tr->offsets_size); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_offset; } if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) { @@ -2132,6 +3047,8 @@ static void binder_transaction(struct binder_proc *proc, proc->pid, thread->pid, (u64)extra_buffers_size); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_offset; } off_end = (void *)off_start + tr->offsets_size; @@ -2148,6 +3065,8 @@ static void binder_transaction(struct binder_proc *proc, (u64)off_min, (u64)t->buffer->data_size); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_offset; } @@ -2162,6 +3081,8 @@ static void binder_transaction(struct binder_proc *proc, ret = binder_translate_binder(fp, t, thread); if (ret < 0) { return_error = BR_FAILED_REPLY; + return_error_param = ret; + return_error_line = __LINE__; goto err_translate_failed; } } break; @@ -2173,6 +3094,8 @@ static void binder_transaction(struct binder_proc *proc, ret = binder_translate_handle(fp, t, thread); if (ret < 0) { return_error = BR_FAILED_REPLY; + return_error_param = ret; + return_error_line = __LINE__; goto err_translate_failed; } } break; @@ -2184,6 +3107,8 @@ static void binder_transaction(struct binder_proc *proc, if (target_fd < 0) { return_error = BR_FAILED_REPLY; + return_error_param = target_fd; + return_error_line = __LINE__; goto err_translate_failed; } fp->pad_binder = 0; @@ -2200,6 +3125,8 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with invalid parent offset or type\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_parent; } if (!binder_validate_fixup(t->buffer, off_start, @@ -2209,12 +3136,16 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_parent; } ret = binder_translate_fd_array(fda, parent, t, thread, in_reply_to); if (ret < 0) { return_error = BR_FAILED_REPLY; + return_error_param = ret; + return_error_line = __LINE__; goto err_translate_failed; } last_fixup_obj = parent; @@ -2230,20 +3161,24 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with too large buffer\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_offset; } - if (copy_from_user_preempt_disabled( - sg_bufp, - (const void __user *)(uintptr_t) - bp->buffer, bp->length)) { + if (copy_from_user(sg_bufp, + (const void __user *)(uintptr_t) + bp->buffer, bp->length)) { binder_user_error("%d:%d got transaction with invalid offsets ptr\n", proc->pid, thread->pid); + return_error_param = -EFAULT; return_error = BR_FAILED_REPLY; + return_error_line = __LINE__; goto err_copy_data_failed; } /* Fixup buffer pointer to target proc address space */ bp->buffer = (uintptr_t)sg_bufp + - target_proc->user_buffer_offset; + binder_alloc_get_user_buffer_offset( + &target_proc->alloc); sg_bufp += ALIGN(bp->length, sizeof(u64)); ret = binder_fixup_parent(t, thread, bp, off_start, @@ -2252,6 +3187,8 @@ static void binder_transaction(struct binder_proc *proc, last_fixup_min_off); if (ret < 0) { return_error = BR_FAILED_REPLY; + return_error_param = ret; + return_error_line = __LINE__; goto err_translate_failed; } last_fixup_obj = bp; @@ -2261,42 +3198,61 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with invalid object type, %x\n", proc->pid, thread->pid, hdr->type); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_object_type; } } + tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; + binder_enqueue_work(proc, tcomplete, &thread->todo); + t->work.type = BINDER_WORK_TRANSACTION; + if (reply) { + binder_inner_proc_lock(target_proc); + if (target_thread->is_dead) { + binder_inner_proc_unlock(target_proc); + goto err_dead_proc_or_thread; + } BUG_ON(t->buffer->async_transaction != 0); - binder_pop_transaction(target_thread, in_reply_to); + binder_pop_transaction_ilocked(target_thread, in_reply_to); + binder_enqueue_work_ilocked(&t->work, &target_thread->todo); + binder_inner_proc_unlock(target_proc); + wake_up_interruptible_sync(&target_thread->wait); + binder_restore_priority(current, in_reply_to->saved_priority); + binder_free_transaction(in_reply_to); } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); + binder_inner_proc_lock(proc); t->need_reply = 1; t->from_parent = thread->transaction_stack; thread->transaction_stack = t; + binder_inner_proc_unlock(proc); + if (!binder_proc_transaction(t, target_proc, target_thread)) { + binder_inner_proc_lock(proc); + binder_pop_transaction_ilocked(thread, t); + binder_inner_proc_unlock(proc); + goto err_dead_proc_or_thread; + } } else { BUG_ON(target_node == NULL); BUG_ON(t->buffer->async_transaction != 1); - if (target_node->has_async_transaction) { - target_list = &target_node->async_todo; - target_wait = NULL; - } else - target_node->has_async_transaction = 1; - } - t->work.type = BINDER_WORK_TRANSACTION; - list_add_tail(&t->work.entry, target_list); - tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; - list_add_tail(&tcomplete->entry, &thread->todo); - if (target_wait) { - if (reply || !(t->flags & TF_ONE_WAY)) { - preempt_disable(); - wake_up_interruptible_sync(target_wait); - preempt_enable_no_resched(); - } - else { - wake_up_interruptible(target_wait); - } + if (!binder_proc_transaction(t, target_proc, NULL)) + goto err_dead_proc_or_thread; } + if (target_thread) + binder_thread_dec_tmpref(target_thread); + binder_proc_dec_tmpref(target_proc); + /* + * write barrier to synchronize with initialization + * of log entry + */ + smp_wmb(); + WRITE_ONCE(e->debug_id_done, t_debug_id); return; +err_dead_proc_or_thread: + return_error = BR_DEAD_REPLY; + return_error_line = __LINE__; err_translate_failed: err_bad_object_type: err_bad_offset: @@ -2304,8 +3260,9 @@ err_bad_parent: err_copy_data_failed: trace_binder_transaction_failed_buffer_release(t->buffer); binder_transaction_buffer_release(target_proc, t->buffer, offp); + target_node = NULL; t->buffer->transaction = NULL; - binder_free_buf(target_proc, t->buffer); + binder_alloc_free_buf(&target_proc->alloc, t->buffer); err_binder_alloc_buf_failed: kfree(tcomplete); binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); @@ -2318,25 +3275,50 @@ err_empty_call_stack: err_dead_binder: err_invalid_target_handle: err_no_context_mgr_node: + if (target_thread) + binder_thread_dec_tmpref(target_thread); + if (target_proc) + binder_proc_dec_tmpref(target_proc); + if (target_node) + binder_dec_node(target_node, 1, 0); + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, - "%d:%d transaction failed %d, size %lld-%lld\n", - proc->pid, thread->pid, return_error, - (u64)tr->data_size, (u64)tr->offsets_size); + "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n", + proc->pid, thread->pid, return_error, return_error_param, + (u64)tr->data_size, (u64)tr->offsets_size, + return_error_line); { struct binder_transaction_log_entry *fe; - fe = binder_transaction_log_add( - &context->transaction_log_failed); + e->return_error = return_error; + e->return_error_param = return_error_param; + e->return_error_line = return_error_line; + fe = binder_transaction_log_add(&binder_transaction_log_failed); *fe = *e; + /* + * write barrier to synchronize with initialization + * of log entry + */ + smp_wmb(); + WRITE_ONCE(e->debug_id_done, t_debug_id); + WRITE_ONCE(fe->debug_id_done, t_debug_id); } - BUG_ON(thread->return_error != BR_OK); + BUG_ON(thread->return_error.cmd != BR_OK); if (in_reply_to) { - thread->return_error = BR_TRANSACTION_COMPLETE; + binder_restore_priority(current, in_reply_to->saved_priority); + thread->return_error.cmd = BR_TRANSACTION_COMPLETE; + binder_enqueue_work(thread->proc, + &thread->return_error.work, + &thread->todo); binder_send_failed_reply(in_reply_to, return_error); - } else - thread->return_error = return_error; + } else { + thread->return_error.cmd = return_error; + binder_enqueue_work(thread->proc, + &thread->return_error.work, + &thread->todo); + } } static int binder_thread_write(struct binder_proc *proc, @@ -2350,15 +3332,17 @@ static int binder_thread_write(struct binder_proc *proc, void __user *ptr = buffer + *consumed; void __user *end = buffer + size; - while (ptr < end && thread->return_error == BR_OK) { - if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr)) + while (ptr < end && thread->return_error.cmd == BR_OK) { + int ret; + + if (get_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); trace_binder_command(cmd); - if (_IOC_NR(cmd) < ARRAY_SIZE(context->binder_stats.bc)) { - context->binder_stats.bc[_IOC_NR(cmd)]++; - proc->stats.bc[_IOC_NR(cmd)]++; - thread->stats.bc[_IOC_NR(cmd)]++; + if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { + atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]); + atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]); + atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]); } switch (cmd) { case BC_INCREFS: @@ -2366,53 +3350,61 @@ static int binder_thread_write(struct binder_proc *proc, case BC_RELEASE: case BC_DECREFS: { uint32_t target; - struct binder_ref *ref; const char *debug_string; + bool strong = cmd == BC_ACQUIRE || cmd == BC_RELEASE; + bool increment = cmd == BC_INCREFS || cmd == BC_ACQUIRE; + struct binder_ref_data rdata; - if (get_user_preempt_disabled(target, (uint32_t __user *)ptr)) + if (get_user(target, (uint32_t __user *)ptr)) return -EFAULT; + ptr += sizeof(uint32_t); - if (target == 0 && context->binder_context_mgr_node && - (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) { - ref = binder_get_ref_for_node(proc, - context->binder_context_mgr_node); - if (ref->desc != target) { - binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n", - proc->pid, thread->pid, - ref->desc); - } - } else - ref = binder_get_ref(proc, target, - cmd == BC_ACQUIRE || - cmd == BC_RELEASE); - if (ref == NULL) { - binder_user_error("%d:%d refcount change on invalid ref %d\n", - proc->pid, thread->pid, target); - break; + ret = -1; + if (increment && !target) { + struct binder_node *ctx_mgr_node; + mutex_lock(&context->context_mgr_node_lock); + ctx_mgr_node = context->binder_context_mgr_node; + if (ctx_mgr_node) + ret = binder_inc_ref_for_node( + proc, ctx_mgr_node, + strong, NULL, &rdata); + mutex_unlock(&context->context_mgr_node_lock); + } + if (ret) + ret = binder_update_ref_for_handle( + proc, target, increment, strong, + &rdata); + if (!ret && rdata.desc != target) { + binder_user_error("%d:%d tried to acquire reference to desc %d, got %d instead\n", + proc->pid, thread->pid, + target, rdata.desc); } switch (cmd) { case BC_INCREFS: debug_string = "IncRefs"; - binder_inc_ref(ref, 0, NULL); break; case BC_ACQUIRE: debug_string = "Acquire"; - binder_inc_ref(ref, 1, NULL); break; case BC_RELEASE: debug_string = "Release"; - binder_dec_ref(ref, 1); break; case BC_DECREFS: default: debug_string = "DecRefs"; - binder_dec_ref(ref, 0); + break; + } + if (ret) { + binder_user_error("%d:%d %s %d refcount change on invalid ref %d ret %d\n", + proc->pid, thread->pid, debug_string, + strong, target, ret); break; } binder_debug(BINDER_DEBUG_USER_REFS, - "%d:%d %s ref %d desc %d s %d w %d for node %d\n", - proc->pid, thread->pid, debug_string, ref->debug_id, - ref->desc, ref->strong, ref->weak, ref->node->debug_id); + "%d:%d %s ref %d desc %d s %d w %d\n", + proc->pid, thread->pid, debug_string, + rdata.debug_id, rdata.desc, rdata.strong, + rdata.weak); break; } case BC_INCREFS_DONE: @@ -2420,11 +3412,12 @@ static int binder_thread_write(struct binder_proc *proc, binder_uintptr_t node_ptr; binder_uintptr_t cookie; struct binder_node *node; + bool free_node; - if (get_user_preempt_disabled(node_ptr, (binder_uintptr_t __user *)ptr)) + if (get_user(node_ptr, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); - if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr)) + if (get_user(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); node = binder_get_node(proc, node_ptr); @@ -2444,13 +3437,17 @@ static int binder_thread_write(struct binder_proc *proc, "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", (u64)node_ptr, node->debug_id, (u64)cookie, (u64)node->cookie); + binder_put_node(node); break; } + binder_node_inner_lock(node); if (cmd == BC_ACQUIRE_DONE) { if (node->pending_strong_ref == 0) { binder_user_error("%d:%d BC_ACQUIRE_DONE node %d has no pending acquire request\n", proc->pid, thread->pid, node->debug_id); + binder_node_inner_unlock(node); + binder_put_node(node); break; } node->pending_strong_ref = 0; @@ -2459,16 +3456,23 @@ static int binder_thread_write(struct binder_proc *proc, binder_user_error("%d:%d BC_INCREFS_DONE node %d has no pending increfs request\n", proc->pid, thread->pid, node->debug_id); + binder_node_inner_unlock(node); + binder_put_node(node); break; } node->pending_weak_ref = 0; } - binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0); + free_node = binder_dec_node_nilocked(node, + cmd == BC_ACQUIRE_DONE, 0); + WARN_ON(free_node); binder_debug(BINDER_DEBUG_USER_REFS, - "%d:%d %s node %d ls %d lw %d\n", + "%d:%d %s node %d ls %d lw %d tr %d\n", proc->pid, thread->pid, cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", - node->debug_id, node->local_strong_refs, node->local_weak_refs); + node->debug_id, node->local_strong_refs, + node->local_weak_refs, node->tmp_refs); + binder_node_inner_unlock(node); + binder_put_node(node); break; } case BC_ATTEMPT_ACQUIRE: @@ -2482,11 +3486,12 @@ static int binder_thread_write(struct binder_proc *proc, binder_uintptr_t data_ptr; struct binder_buffer *buffer; - if (get_user_preempt_disabled(data_ptr, (binder_uintptr_t __user *)ptr)) + if (get_user(data_ptr, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); - buffer = binder_buffer_lookup(proc, data_ptr); + buffer = binder_alloc_prepare_to_free(&proc->alloc, + data_ptr); if (buffer == NULL) { binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n", proc->pid, thread->pid, (u64)data_ptr); @@ -2508,15 +3513,25 @@ static int binder_thread_write(struct binder_proc *proc, buffer->transaction = NULL; } if (buffer->async_transaction && buffer->target_node) { - BUG_ON(!buffer->target_node->has_async_transaction); - if (list_empty(&buffer->target_node->async_todo)) - buffer->target_node->has_async_transaction = 0; + struct binder_node *buf_node; + struct binder_work *w; + + buf_node = buffer->target_node; + binder_node_inner_lock(buf_node); + BUG_ON(!buf_node->has_async_transaction); + BUG_ON(buf_node->proc != proc); + w = binder_dequeue_work_head_ilocked( + &buf_node->async_todo); + if (!w) + buf_node->has_async_transaction = 0; else - list_move_tail(buffer->target_node->async_todo.next, &thread->todo); + binder_enqueue_work_ilocked( + w, &thread->todo); + binder_node_inner_unlock(buf_node); } trace_binder_transaction_buffer_release(buffer); binder_transaction_buffer_release(proc, buffer, NULL); - binder_free_buf(proc, buffer); + binder_alloc_free_buf(&proc->alloc, buffer); break; } @@ -2524,8 +3539,7 @@ static int binder_thread_write(struct binder_proc *proc, case BC_REPLY_SG: { struct binder_transaction_data_sg tr; - if (copy_from_user_preempt_disabled(&tr, ptr, - sizeof(tr))) + if (copy_from_user(&tr, ptr, sizeof(tr))) return -EFAULT; ptr += sizeof(tr); binder_transaction(proc, thread, &tr.transaction_data, @@ -2536,7 +3550,7 @@ static int binder_thread_write(struct binder_proc *proc, case BC_REPLY: { struct binder_transaction_data tr; - if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr))) + if (copy_from_user(&tr, ptr, sizeof(tr))) return -EFAULT; ptr += sizeof(tr); binder_transaction(proc, thread, &tr, @@ -2548,6 +3562,7 @@ static int binder_thread_write(struct binder_proc *proc, binder_debug(BINDER_DEBUG_THREADS, "%d:%d BC_REGISTER_LOOPER\n", proc->pid, thread->pid); + binder_inner_proc_lock(proc); if (thread->looper & BINDER_LOOPER_STATE_ENTERED) { thread->looper |= BINDER_LOOPER_STATE_INVALID; binder_user_error("%d:%d ERROR: BC_REGISTER_LOOPER called after BC_ENTER_LOOPER\n", @@ -2561,6 +3576,7 @@ static int binder_thread_write(struct binder_proc *proc, proc->requested_threads_started++; } thread->looper |= BINDER_LOOPER_STATE_REGISTERED; + binder_inner_proc_unlock(proc); break; case BC_ENTER_LOOPER: binder_debug(BINDER_DEBUG_THREADS, @@ -2585,15 +3601,37 @@ static int binder_thread_write(struct binder_proc *proc, uint32_t target; binder_uintptr_t cookie; struct binder_ref *ref; - struct binder_ref_death *death; + struct binder_ref_death *death = NULL; - if (get_user_preempt_disabled(target, (uint32_t __user *)ptr)) + if (get_user(target, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); - if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr)) + if (get_user(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); - ref = binder_get_ref(proc, target, false); + if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { + /* + * Allocate memory for death notification + * before taking lock + */ + death = kzalloc(sizeof(*death), GFP_KERNEL); + if (death == NULL) { + WARN_ON(thread->return_error.cmd != + BR_OK); + thread->return_error.cmd = BR_ERROR; + binder_enqueue_work( + thread->proc, + &thread->return_error.work, + &thread->todo); + binder_debug( + BINDER_DEBUG_FAILED_TRANSACTION, + "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n", + proc->pid, thread->pid); + break; + } + } + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, target, false); if (ref == NULL) { binder_user_error("%d:%d %s invalid ref %d\n", proc->pid, thread->pid, @@ -2601,6 +3639,8 @@ static int binder_thread_write(struct binder_proc *proc, "BC_REQUEST_DEATH_NOTIFICATION" : "BC_CLEAR_DEATH_NOTIFICATION", target); + binder_proc_unlock(proc); + kfree(death); break; } @@ -2610,21 +3650,18 @@ static int binder_thread_write(struct binder_proc *proc, cmd == BC_REQUEST_DEATH_NOTIFICATION ? "BC_REQUEST_DEATH_NOTIFICATION" : "BC_CLEAR_DEATH_NOTIFICATION", - (u64)cookie, ref->debug_id, ref->desc, - ref->strong, ref->weak, ref->node->debug_id); + (u64)cookie, ref->data.debug_id, + ref->data.desc, ref->data.strong, + ref->data.weak, ref->node->debug_id); + binder_node_lock(ref->node); if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { if (ref->death) { binder_user_error("%d:%d BC_REQUEST_DEATH_NOTIFICATION death notification already set\n", proc->pid, thread->pid); - break; - } - death = kzalloc_preempt_disabled(sizeof(*death)); - if (death == NULL) { - thread->return_error = BR_ERROR; - binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, - "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n", - proc->pid, thread->pid); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + kfree(death); break; } binder_stats_created(BINDER_STAT_DEATH); @@ -2633,17 +3670,29 @@ static int binder_thread_write(struct binder_proc *proc, ref->death = death; if (ref->node->proc == NULL) { ref->death->work.type = BINDER_WORK_DEAD_BINDER; - if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { - list_add_tail(&ref->death->work.entry, &thread->todo); - } else { - list_add_tail(&ref->death->work.entry, &proc->todo); - wake_up_interruptible(&proc->wait); + if (thread->looper & + (BINDER_LOOPER_STATE_REGISTERED | + BINDER_LOOPER_STATE_ENTERED)) + binder_enqueue_work( + proc, + &ref->death->work, + &thread->todo); + else { + binder_inner_proc_lock(proc); + binder_enqueue_work_ilocked( + &ref->death->work, + &proc->todo); + binder_wakeup_proc_ilocked( + proc); + binder_inner_proc_unlock(proc); } } } else { if (ref->death == NULL) { binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification not active\n", proc->pid, thread->pid); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); break; } death = ref->death; @@ -2652,33 +3701,52 @@ static int binder_thread_write(struct binder_proc *proc, proc->pid, thread->pid, (u64)death->cookie, (u64)cookie); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); break; } ref->death = NULL; + binder_inner_proc_lock(proc); if (list_empty(&death->work.entry)) { death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; - if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { - list_add_tail(&death->work.entry, &thread->todo); - } else { - list_add_tail(&death->work.entry, &proc->todo); - wake_up_interruptible(&proc->wait); + if (thread->looper & + (BINDER_LOOPER_STATE_REGISTERED | + BINDER_LOOPER_STATE_ENTERED)) + binder_enqueue_work_ilocked( + &death->work, + &thread->todo); + else { + binder_enqueue_work_ilocked( + &death->work, + &proc->todo); + binder_wakeup_proc_ilocked( + proc); } } else { BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER); death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR; } + binder_inner_proc_unlock(proc); } + binder_node_unlock(ref->node); + binder_proc_unlock(proc); } break; case BC_DEAD_BINDER_DONE: { struct binder_work *w; binder_uintptr_t cookie; struct binder_ref_death *death = NULL; - if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr)) + + if (get_user(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(cookie); - list_for_each_entry(w, &proc->delivered_death, entry) { - struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work); + binder_inner_proc_lock(proc); + list_for_each_entry(w, &proc->delivered_death, + entry) { + struct binder_ref_death *tmp_death = + container_of(w, + struct binder_ref_death, + work); if (tmp_death->cookie == cookie) { death = tmp_death; @@ -2692,21 +3760,26 @@ static int binder_thread_write(struct binder_proc *proc, if (death == NULL) { binder_user_error("%d:%d BC_DEAD_BINDER_DONE %016llx not found\n", proc->pid, thread->pid, (u64)cookie); + binder_inner_proc_unlock(proc); break; } - - list_del_init(&death->work.entry); + binder_dequeue_work_ilocked(&death->work); if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) { death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; - if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { - list_add_tail(&death->work.entry, &thread->todo); - } else { - list_add_tail(&death->work.entry, &proc->todo); - wake_up_interruptible(&proc->wait); + if (thread->looper & + (BINDER_LOOPER_STATE_REGISTERED | + BINDER_LOOPER_STATE_ENTERED)) + binder_enqueue_work_ilocked( + &death->work, &thread->todo); + else { + binder_enqueue_work_ilocked( + &death->work, + &proc->todo); + binder_wakeup_proc_ilocked(proc); } } - } - break; + binder_inner_proc_unlock(proc); + } break; default: pr_err("%d:%d unknown command %d\n", @@ -2722,24 +3795,80 @@ static void binder_stat_br(struct binder_proc *proc, struct binder_thread *thread, uint32_t cmd) { trace_binder_return(cmd); - if (_IOC_NR(cmd) < ARRAY_SIZE(proc->stats.br)) { - proc->context->binder_stats.br[_IOC_NR(cmd)]++; - proc->stats.br[_IOC_NR(cmd)]++; - thread->stats.br[_IOC_NR(cmd)]++; + if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) { + atomic_inc(&binder_stats.br[_IOC_NR(cmd)]); + atomic_inc(&proc->stats.br[_IOC_NR(cmd)]); + atomic_inc(&thread->stats.br[_IOC_NR(cmd)]); } } -static int binder_has_proc_work(struct binder_proc *proc, - struct binder_thread *thread) +static int binder_has_thread_work(struct binder_thread *thread) { - return !list_empty(&proc->todo) || - (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); + return !binder_worklist_empty(thread->proc, &thread->todo) || + thread->looper_need_return; } -static int binder_has_thread_work(struct binder_thread *thread) +static int binder_put_node_cmd(struct binder_proc *proc, + struct binder_thread *thread, + void __user **ptrp, + binder_uintptr_t node_ptr, + binder_uintptr_t node_cookie, + int node_debug_id, + uint32_t cmd, const char *cmd_name) { - return !list_empty(&thread->todo) || thread->return_error != BR_OK || - (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); + void __user *ptr = *ptrp; + + if (put_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + + if (put_user(node_ptr, (binder_uintptr_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(binder_uintptr_t); + + if (put_user(node_cookie, (binder_uintptr_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(binder_uintptr_t); + + binder_stat_br(proc, thread, cmd); + binder_debug(BINDER_DEBUG_USER_REFS, "%d:%d %s %d u%016llx c%016llx\n", + proc->pid, thread->pid, cmd_name, node_debug_id, + (u64)node_ptr, (u64)node_cookie); + + *ptrp = ptr; + return 0; +} + +static int binder_wait_for_work(struct binder_thread *thread, + bool do_proc_work) +{ + DEFINE_WAIT(wait); + struct binder_proc *proc = thread->proc; + int ret = 0; + + freezer_do_not_count(); + binder_inner_proc_lock(proc); + for (;;) { + prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE); + if (binder_has_work_ilocked(thread, do_proc_work)) + break; + if (do_proc_work) + list_add(&thread->waiting_thread_node, + &proc->waiting_threads); + binder_inner_proc_unlock(proc); + schedule(); + binder_inner_proc_lock(proc); + list_del_init(&thread->waiting_thread_node); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + finish_wait(&thread->wait, &wait); + binder_inner_proc_unlock(proc); + freezer_count(); + + return ret; } static int binder_thread_read(struct binder_proc *proc, @@ -2755,43 +3884,21 @@ static int binder_thread_read(struct binder_proc *proc, int wait_for_proc_work; if (*consumed == 0) { - if (put_user_preempt_disabled(BR_NOOP, (uint32_t __user *)ptr)) + if (put_user(BR_NOOP, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); } retry: - wait_for_proc_work = thread->transaction_stack == NULL && - list_empty(&thread->todo); - - if (thread->return_error != BR_OK && ptr < end) { - if (thread->return_error2 != BR_OK) { - if (put_user_preempt_disabled(thread->return_error2, (uint32_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(uint32_t); - binder_stat_br(proc, thread, thread->return_error2); - if (ptr == end) - goto done; - thread->return_error2 = BR_OK; - } - if (put_user_preempt_disabled(thread->return_error, (uint32_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(uint32_t); - binder_stat_br(proc, thread, thread->return_error); - thread->return_error = BR_OK; - goto done; - } - + binder_inner_proc_lock(proc); + wait_for_proc_work = binder_available_for_proc_work_ilocked(thread); + binder_inner_proc_unlock(proc); thread->looper |= BINDER_LOOPER_STATE_WAITING; - if (wait_for_proc_work) - proc->ready_threads++; - - binder_unlock(proc->context, __func__); trace_binder_wait_for_work(wait_for_proc_work, !!thread->transaction_stack, - !list_empty(&thread->todo)); + !binder_worklist_empty(proc, &thread->todo)); if (wait_for_proc_work) { if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) { @@ -2800,24 +3907,16 @@ retry: wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); } - binder_set_nice(proc->default_priority); - if (non_block) { - if (!binder_has_proc_work(proc, thread)) - ret = -EAGAIN; - } else - ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); - } else { - if (non_block) { - if (!binder_has_thread_work(thread)) - ret = -EAGAIN; - } else - ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); + binder_restore_priority(current, proc->default_priority); } - binder_lock(proc->context, __func__); + if (non_block) { + if (!binder_has_work(thread, wait_for_proc_work)) + ret = -EAGAIN; + } else { + ret = binder_wait_for_work(thread, wait_for_proc_work); + } - if (wait_for_proc_work) - proc->ready_threads--; thread->looper &= ~BINDER_LOOPER_STATE_WAITING; if (ret) @@ -2826,33 +3925,54 @@ retry: while (1) { uint32_t cmd; struct binder_transaction_data tr; - struct binder_work *w; + struct binder_work *w = NULL; + struct list_head *list = NULL; struct binder_transaction *t = NULL; + struct binder_thread *t_from; + + binder_inner_proc_lock(proc); + if (!binder_worklist_empty_ilocked(&thread->todo)) + list = &thread->todo; + else if (!binder_worklist_empty_ilocked(&proc->todo) && + wait_for_proc_work) + list = &proc->todo; + else { + binder_inner_proc_unlock(proc); - if (!list_empty(&thread->todo)) { - w = list_first_entry(&thread->todo, struct binder_work, - entry); - } else if (!list_empty(&proc->todo) && wait_for_proc_work) { - w = list_first_entry(&proc->todo, struct binder_work, - entry); - } else { /* no data added */ - if (ptr - buffer == 4 && - !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) + if (ptr - buffer == 4 && !thread->looper_need_return) goto retry; break; } - if (end - ptr < sizeof(tr) + 4) + if (end - ptr < sizeof(tr) + 4) { + binder_inner_proc_unlock(proc); break; + } + w = binder_dequeue_work_head_ilocked(list); switch (w->type) { case BINDER_WORK_TRANSACTION: { + binder_inner_proc_unlock(proc); t = container_of(w, struct binder_transaction, work); } break; + case BINDER_WORK_RETURN_ERROR: { + struct binder_error *e = container_of( + w, struct binder_error, work); + + WARN_ON(e->cmd == BR_OK); + binder_inner_proc_unlock(proc); + if (put_user(e->cmd, (uint32_t __user *)ptr)) + return -EFAULT; + e->cmd = BR_OK; + ptr += sizeof(uint32_t); + + binder_stat_br(proc, thread, cmd); + } break; case BINDER_WORK_TRANSACTION_COMPLETE: { + binder_inner_proc_unlock(proc); cmd = BR_TRANSACTION_COMPLETE; - if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) + if (put_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); @@ -2860,112 +3980,134 @@ retry: binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE, "%d:%d BR_TRANSACTION_COMPLETE\n", proc->pid, thread->pid); - - list_del(&w->entry); kfree(w); binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); } break; case BINDER_WORK_NODE: { struct binder_node *node = container_of(w, struct binder_node, work); - uint32_t cmd = BR_NOOP; - const char *cmd_name; - int strong = node->internal_strong_refs || node->local_strong_refs; - int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong; - - if (weak && !node->has_weak_ref) { - cmd = BR_INCREFS; - cmd_name = "BR_INCREFS"; + int strong, weak; + binder_uintptr_t node_ptr = node->ptr; + binder_uintptr_t node_cookie = node->cookie; + int node_debug_id = node->debug_id; + int has_weak_ref; + int has_strong_ref; + void __user *orig_ptr = ptr; + + BUG_ON(proc != node->proc); + strong = node->internal_strong_refs || + node->local_strong_refs; + weak = !hlist_empty(&node->refs) || + node->local_weak_refs || + node->tmp_refs || strong; + has_strong_ref = node->has_strong_ref; + has_weak_ref = node->has_weak_ref; + + if (weak && !has_weak_ref) { node->has_weak_ref = 1; node->pending_weak_ref = 1; node->local_weak_refs++; - } else if (strong && !node->has_strong_ref) { - cmd = BR_ACQUIRE; - cmd_name = "BR_ACQUIRE"; + } + if (strong && !has_strong_ref) { node->has_strong_ref = 1; node->pending_strong_ref = 1; node->local_strong_refs++; - } else if (!strong && node->has_strong_ref) { - cmd = BR_RELEASE; - cmd_name = "BR_RELEASE"; + } + if (!strong && has_strong_ref) node->has_strong_ref = 0; - } else if (!weak && node->has_weak_ref) { - cmd = BR_DECREFS; - cmd_name = "BR_DECREFS"; + if (!weak && has_weak_ref) node->has_weak_ref = 0; - } - if (cmd != BR_NOOP) { - if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) - return -EFAULT; - ptr += sizeof(uint32_t); - if (put_user_preempt_disabled(node->ptr, (binder_uintptr_t __user *) - (binder_uintptr_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(binder_uintptr_t); - if (put_user_preempt_disabled(node->cookie, (binder_uintptr_t __user *) - (binder_uintptr_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(binder_uintptr_t); - - binder_stat_br(proc, thread, cmd); - binder_debug(BINDER_DEBUG_USER_REFS, - "%d:%d %s %d u%016llx c%016llx\n", - proc->pid, thread->pid, cmd_name, - node->debug_id, - (u64)node->ptr, (u64)node->cookie); - } else { - list_del_init(&w->entry); - if (!weak && !strong) { - binder_debug(BINDER_DEBUG_INTERNAL_REFS, - "%d:%d node %d u%016llx c%016llx deleted\n", - proc->pid, thread->pid, - node->debug_id, - (u64)node->ptr, - (u64)node->cookie); - rb_erase(&node->rb_node, &proc->nodes); - kfree(node); - binder_stats_deleted(BINDER_STAT_NODE); - } else { - binder_debug(BINDER_DEBUG_INTERNAL_REFS, - "%d:%d node %d u%016llx c%016llx state unchanged\n", - proc->pid, thread->pid, - node->debug_id, - (u64)node->ptr, - (u64)node->cookie); - } - } + if (!weak && !strong) { + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "%d:%d node %d u%016llx c%016llx deleted\n", + proc->pid, thread->pid, + node_debug_id, + (u64)node_ptr, + (u64)node_cookie); + rb_erase(&node->rb_node, &proc->nodes); + binder_inner_proc_unlock(proc); + binder_node_lock(node); + /* + * Acquire the node lock before freeing the + * node to serialize with other threads that + * may have been holding the node lock while + * decrementing this node (avoids race where + * this thread frees while the other thread + * is unlocking the node after the final + * decrement) + */ + binder_node_unlock(node); + binder_free_node(node); + } else + binder_inner_proc_unlock(proc); + + if (weak && !has_weak_ref) + ret = binder_put_node_cmd( + proc, thread, &ptr, node_ptr, + node_cookie, node_debug_id, + BR_INCREFS, "BR_INCREFS"); + if (!ret && strong && !has_strong_ref) + ret = binder_put_node_cmd( + proc, thread, &ptr, node_ptr, + node_cookie, node_debug_id, + BR_ACQUIRE, "BR_ACQUIRE"); + if (!ret && !strong && has_strong_ref) + ret = binder_put_node_cmd( + proc, thread, &ptr, node_ptr, + node_cookie, node_debug_id, + BR_RELEASE, "BR_RELEASE"); + if (!ret && !weak && has_weak_ref) + ret = binder_put_node_cmd( + proc, thread, &ptr, node_ptr, + node_cookie, node_debug_id, + BR_DECREFS, "BR_DECREFS"); + if (orig_ptr == ptr) + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "%d:%d node %d u%016llx c%016llx state unchanged\n", + proc->pid, thread->pid, + node_debug_id, + (u64)node_ptr, + (u64)node_cookie); + if (ret) + return ret; } break; case BINDER_WORK_DEAD_BINDER: case BINDER_WORK_DEAD_BINDER_AND_CLEAR: case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: { struct binder_ref_death *death; uint32_t cmd; + binder_uintptr_t cookie; death = container_of(w, struct binder_ref_death, work); if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE; else cmd = BR_DEAD_BINDER; - if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) - return -EFAULT; - ptr += sizeof(uint32_t); - if (put_user_preempt_disabled(death->cookie, (binder_uintptr_t __user *) ptr)) - return -EFAULT; - ptr += sizeof(binder_uintptr_t); - binder_stat_br(proc, thread, cmd); + cookie = death->cookie; + binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION, "%d:%d %s %016llx\n", proc->pid, thread->pid, cmd == BR_DEAD_BINDER ? "BR_DEAD_BINDER" : "BR_CLEAR_DEATH_NOTIFICATION_DONE", - (u64)death->cookie); - + (u64)cookie); if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) { - list_del(&w->entry); + binder_inner_proc_unlock(proc); kfree(death); binder_stats_deleted(BINDER_STAT_DEATH); - } else - list_move(&w->entry, &proc->delivered_death); + } else { + binder_enqueue_work_ilocked( + w, &proc->delivered_death); + binder_inner_proc_unlock(proc); + } + if (put_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (put_user(cookie, + (binder_uintptr_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(binder_uintptr_t); + binder_stat_br(proc, thread, cmd); if (cmd == BR_DEAD_BINDER) goto done; /* DEAD_BINDER notifications can cause transactions */ } break; @@ -2977,16 +4119,14 @@ retry: BUG_ON(t->buffer == NULL); if (t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; + struct binder_priority node_prio; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; - t->saved_priority = task_nice(current); - if (t->priority < target_node->min_priority && - !(t->flags & TF_ONE_WAY)) - binder_set_nice(t->priority); - else if (!(t->flags & TF_ONE_WAY) || - t->saved_priority > target_node->min_priority) - binder_set_nice(target_node->min_priority); + node_prio.sched_policy = target_node->sched_policy; + node_prio.prio = target_node->min_priority; + binder_transaction_priority(current, t, node_prio, + target_node->inherit_rt); cmd = BR_TRANSACTION; } else { tr.target.ptr = 0; @@ -2997,8 +4137,9 @@ retry: tr.flags = t->flags; tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid); - if (t->from) { - struct task_struct *sender = t->from->proc->tsk; + t_from = binder_get_txn_from(t); + if (t_from) { + struct task_struct *sender = t_from->proc->tsk; tr.sender_pid = task_tgid_nr_ns(sender, task_active_pid_ns(current)); @@ -3008,18 +4149,24 @@ retry: tr.data_size = t->buffer->data_size; tr.offsets_size = t->buffer->offsets_size; - tr.data.ptr.buffer = (binder_uintptr_t)( - (uintptr_t)t->buffer->data + - proc->user_buffer_offset); + tr.data.ptr.buffer = (binder_uintptr_t) + ((uintptr_t)t->buffer->data + + binder_alloc_get_user_buffer_offset(&proc->alloc)); tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *)); - if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) + if (put_user(cmd, (uint32_t __user *)ptr)) { + if (t_from) + binder_thread_dec_tmpref(t_from); return -EFAULT; + } ptr += sizeof(uint32_t); - if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr))) + if (copy_to_user(ptr, &tr, sizeof(tr))) { + if (t_from) + binder_thread_dec_tmpref(t_from); return -EFAULT; + } ptr += sizeof(tr); trace_binder_transaction_received(t); @@ -3029,21 +4176,22 @@ retry: proc->pid, thread->pid, (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" : "BR_REPLY", - t->debug_id, t->from ? t->from->proc->pid : 0, - t->from ? t->from->pid : 0, cmd, + t->debug_id, t_from ? t_from->proc->pid : 0, + t_from ? t_from->pid : 0, cmd, t->buffer->data_size, t->buffer->offsets_size, (u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets); - list_del(&t->work.entry); + if (t_from) + binder_thread_dec_tmpref(t_from); t->buffer->allow_user_free = 1; if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { + binder_inner_proc_lock(thread->proc); t->to_parent = thread->transaction_stack; t->to_thread = thread; thread->transaction_stack = t; + binder_inner_proc_unlock(thread->proc); } else { - t->buffer->transaction = NULL; - kfree(t); - binder_stats_deleted(BINDER_STAT_TRANSACTION); + binder_free_transaction(t); } break; } @@ -3051,29 +4199,36 @@ retry: done: *consumed = ptr - buffer; - if (proc->requested_threads + proc->ready_threads == 0 && + binder_inner_proc_lock(proc); + if (proc->requested_threads == 0 && + list_empty(&thread->proc->waiting_threads) && proc->requested_threads_started < proc->max_threads && (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */ /*spawn a new thread if we leave this out */) { proc->requested_threads++; + binder_inner_proc_unlock(proc); binder_debug(BINDER_DEBUG_THREADS, "%d:%d BR_SPAWN_LOOPER\n", proc->pid, thread->pid); - if (put_user_preempt_disabled(BR_SPAWN_LOOPER, (uint32_t __user *) buffer)) + if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) return -EFAULT; binder_stat_br(proc, thread, BR_SPAWN_LOOPER); - } + } else + binder_inner_proc_unlock(proc); return 0; } -static void binder_release_work(struct list_head *list) +static void binder_release_work(struct binder_proc *proc, + struct list_head *list) { struct binder_work *w; - while (!list_empty(list)) { - w = list_first_entry(list, struct binder_work, entry); - list_del_init(&w->entry); + while (1) { + w = binder_dequeue_work_head(proc, list); + if (!w) + return; + switch (w->type) { case BINDER_WORK_TRANSACTION: { struct binder_transaction *t; @@ -3086,11 +4241,17 @@ static void binder_release_work(struct list_head *list) binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, "undelivered transaction %d\n", t->debug_id); - t->buffer->transaction = NULL; - kfree(t); - binder_stats_deleted(BINDER_STAT_TRANSACTION); + binder_free_transaction(t); } } break; + case BINDER_WORK_RETURN_ERROR: { + struct binder_error *e = container_of( + w, struct binder_error, work); + + binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, + "undelivered TRANSACTION_ERROR: %u\n", + e->cmd); + } break; case BINDER_WORK_TRANSACTION_COMPLETE: { binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, "undelivered TRANSACTION_COMPLETE\n"); @@ -3117,7 +4278,8 @@ static void binder_release_work(struct list_head *list) } -static struct binder_thread *binder_get_thread(struct binder_proc *proc) +static struct binder_thread *binder_get_thread_ilocked( + struct binder_proc *proc, struct binder_thread *new_thread) { struct binder_thread *thread = NULL; struct rb_node *parent = NULL; @@ -3132,38 +4294,102 @@ static struct binder_thread *binder_get_thread(struct binder_proc *proc) else if (current->pid > thread->pid) p = &(*p)->rb_right; else - break; + return thread; } - if (*p == NULL) { - thread = kzalloc_preempt_disabled(sizeof(*thread)); - if (thread == NULL) + if (!new_thread) + return NULL; + thread = new_thread; + binder_stats_created(BINDER_STAT_THREAD); + thread->proc = proc; + thread->pid = current->pid; + get_task_struct(current); + thread->task = current; + atomic_set(&thread->tmp_ref, 0); + init_waitqueue_head(&thread->wait); + INIT_LIST_HEAD(&thread->todo); + rb_link_node(&thread->rb_node, parent, p); + rb_insert_color(&thread->rb_node, &proc->threads); + thread->looper_need_return = true; + thread->return_error.work.type = BINDER_WORK_RETURN_ERROR; + thread->return_error.cmd = BR_OK; + thread->reply_error.work.type = BINDER_WORK_RETURN_ERROR; + thread->reply_error.cmd = BR_OK; + INIT_LIST_HEAD(&new_thread->waiting_thread_node); + return thread; +} + +static struct binder_thread *binder_get_thread(struct binder_proc *proc) +{ + struct binder_thread *thread; + struct binder_thread *new_thread; + + binder_inner_proc_lock(proc); + thread = binder_get_thread_ilocked(proc, NULL); + binder_inner_proc_unlock(proc); + if (!thread) { + new_thread = kzalloc(sizeof(*thread), GFP_KERNEL); + if (new_thread == NULL) return NULL; - binder_stats_created(BINDER_STAT_THREAD); - thread->proc = proc; - thread->pid = current->pid; - init_waitqueue_head(&thread->wait); - INIT_LIST_HEAD(&thread->todo); - rb_link_node(&thread->rb_node, parent, p); - rb_insert_color(&thread->rb_node, &proc->threads); - thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; - thread->return_error = BR_OK; - thread->return_error2 = BR_OK; + binder_inner_proc_lock(proc); + thread = binder_get_thread_ilocked(proc, new_thread); + binder_inner_proc_unlock(proc); + if (thread != new_thread) + kfree(new_thread); } return thread; } -static int binder_free_thread(struct binder_proc *proc, - struct binder_thread *thread) +static void binder_free_proc(struct binder_proc *proc) +{ + BUG_ON(!list_empty(&proc->todo)); + BUG_ON(!list_empty(&proc->delivered_death)); + binder_alloc_deferred_release(&proc->alloc); + put_task_struct(proc->tsk); + binder_stats_deleted(BINDER_STAT_PROC); + kfree(proc); +} + +static void binder_free_thread(struct binder_thread *thread) +{ + BUG_ON(!list_empty(&thread->todo)); + binder_stats_deleted(BINDER_STAT_THREAD); + binder_proc_dec_tmpref(thread->proc); + put_task_struct(thread->task); + kfree(thread); +} + +static int binder_thread_release(struct binder_proc *proc, + struct binder_thread *thread) { struct binder_transaction *t; struct binder_transaction *send_reply = NULL; int active_transactions = 0; + struct binder_transaction *last_t = NULL; + binder_inner_proc_lock(thread->proc); + /* + * take a ref on the proc so it survives + * after we remove this thread from proc->threads. + * The corresponding dec is when we actually + * free the thread in binder_free_thread() + */ + proc->tmp_ref++; + /* + * take a ref on this thread to ensure it + * survives while we are releasing it + */ + atomic_inc(&thread->tmp_ref); rb_erase(&thread->rb_node, &proc->threads); t = thread->transaction_stack; - if (t && t->to_thread == thread) - send_reply = t; + if (t) { + spin_lock(&t->lock); + if (t->to_thread == thread) + send_reply = t; + } + thread->is_dead = true; + while (t) { + last_t = t; active_transactions++; binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, "release %d:%d transaction %d %s, still active\n", @@ -3184,12 +4410,16 @@ static int binder_free_thread(struct binder_proc *proc, t = t->from_parent; } else BUG(); + spin_unlock(&last_t->lock); + if (t) + spin_lock(&t->lock); } + binder_inner_proc_unlock(thread->proc); + if (send_reply) binder_send_failed_reply(send_reply, BR_DEAD_REPLY); - binder_release_work(&thread->todo); - kfree(thread); - binder_stats_deleted(BINDER_STAT_THREAD); + binder_release_work(proc, &thread->todo); + binder_thread_dec_tmpref(thread); return active_transactions; } @@ -3198,30 +4428,24 @@ static unsigned int binder_poll(struct file *filp, { struct binder_proc *proc = filp->private_data; struct binder_thread *thread = NULL; - int wait_for_proc_work; - - binder_lock(proc->context, __func__); + bool wait_for_proc_work; thread = binder_get_thread(proc); - wait_for_proc_work = thread->transaction_stack == NULL && - list_empty(&thread->todo) && thread->return_error == BR_OK; + binder_inner_proc_lock(thread->proc); + thread->looper |= BINDER_LOOPER_STATE_POLL; + wait_for_proc_work = binder_available_for_proc_work_ilocked(thread); - binder_unlock(proc->context, __func__); + binder_inner_proc_unlock(thread->proc); + + if (binder_has_work(thread, wait_for_proc_work)) + return POLLIN; + + poll_wait(filp, &thread->wait, wait); + + if (binder_has_thread_work(thread)) + return POLLIN; - if (wait_for_proc_work) { - if (binder_has_proc_work(proc, thread)) - return POLLIN; - poll_wait(filp, &proc->wait, wait); - if (binder_has_proc_work(proc, thread)) - return POLLIN; - } else { - if (binder_has_thread_work(thread)) - return POLLIN; - poll_wait(filp, &thread->wait, wait); - if (binder_has_thread_work(thread)) - return POLLIN; - } return 0; } @@ -3239,7 +4463,7 @@ static int binder_ioctl_write_read(struct file *filp, ret = -EINVAL; goto out; } - if (copy_from_user_preempt_disabled(&bwr, ubuf, sizeof(bwr))) { + if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { ret = -EFAULT; goto out; } @@ -3257,7 +4481,7 @@ static int binder_ioctl_write_read(struct file *filp, trace_binder_write_done(ret); if (ret < 0) { bwr.read_consumed = 0; - if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) + if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } @@ -3268,10 +4492,12 @@ static int binder_ioctl_write_read(struct file *filp, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); trace_binder_read_done(ret); - if (!list_empty(&proc->todo)) - wake_up_interruptible(&proc->wait); + binder_inner_proc_lock(proc); + if (!binder_worklist_empty_ilocked(&proc->todo)) + binder_wakeup_proc_ilocked(proc); + binder_inner_proc_unlock(proc); if (ret < 0) { - if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) + if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } @@ -3281,7 +4507,7 @@ static int binder_ioctl_write_read(struct file *filp, proc->pid, thread->pid, (u64)bwr.write_consumed, (u64)bwr.write_size, (u64)bwr.read_consumed, (u64)bwr.read_size); - if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) { + if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { ret = -EFAULT; goto out; } @@ -3294,9 +4520,10 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) int ret = 0; struct binder_proc *proc = filp->private_data; struct binder_context *context = proc->context; - + struct binder_node *new_node; kuid_t curr_euid = current_euid(); + mutex_lock(&context->context_mgr_node_lock); if (context->binder_context_mgr_node) { pr_err("BINDER_SET_CONTEXT_MGR already set\n"); ret = -EBUSY; @@ -3317,24 +4544,52 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) } else { context->binder_context_mgr_uid = curr_euid; } - context->binder_context_mgr_node = binder_new_node(proc, 0, 0); - if (!context->binder_context_mgr_node) { + new_node = binder_new_node(proc, NULL); + if (!new_node) { ret = -ENOMEM; goto out; } - context->binder_context_mgr_node->local_weak_refs++; - context->binder_context_mgr_node->local_strong_refs++; - context->binder_context_mgr_node->has_strong_ref = 1; - context->binder_context_mgr_node->has_weak_ref = 1; + binder_node_lock(new_node); + new_node->local_weak_refs++; + new_node->local_strong_refs++; + new_node->has_strong_ref = 1; + new_node->has_weak_ref = 1; + context->binder_context_mgr_node = new_node; + binder_node_unlock(new_node); + binder_put_node(new_node); out: + mutex_unlock(&context->context_mgr_node_lock); return ret; } +static int binder_ioctl_get_node_debug_info(struct binder_proc *proc, + struct binder_node_debug_info *info) { + struct rb_node *n; + binder_uintptr_t ptr = info->ptr; + + memset(info, 0, sizeof(*info)); + + binder_inner_proc_lock(proc); + for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) { + struct binder_node *node = rb_entry(n, struct binder_node, + rb_node); + if (node->ptr > ptr) { + info->ptr = node->ptr; + info->cookie = node->cookie; + info->has_strong_ref = node->has_strong_ref; + info->has_weak_ref = node->has_weak_ref; + break; + } + } + binder_inner_proc_unlock(proc); + + return 0; +} + static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret; struct binder_proc *proc = filp->private_data; - struct binder_context *context = proc->context; struct binder_thread *thread; unsigned int size = _IOC_SIZE(cmd); void __user *ubuf = (void __user *)arg; @@ -3348,7 +4603,6 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ret) goto err_unlocked; - binder_lock(context, __func__); thread = binder_get_thread(proc); if (thread == NULL) { ret = -ENOMEM; @@ -3361,12 +4615,19 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ret) goto err; break; - case BINDER_SET_MAX_THREADS: - if (copy_from_user_preempt_disabled(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { + case BINDER_SET_MAX_THREADS: { + int max_threads; + + if (copy_from_user(&max_threads, ubuf, + sizeof(max_threads))) { ret = -EINVAL; goto err; } + binder_inner_proc_lock(proc); + proc->max_threads = max_threads; + binder_inner_proc_unlock(proc); break; + } case BINDER_SET_CONTEXT_MGR: ret = binder_ioctl_set_ctx_mgr(filp); if (ret) @@ -3375,7 +4636,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case BINDER_THREAD_EXIT: binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n", proc->pid, thread->pid); - binder_free_thread(proc, thread); + binder_thread_release(proc, thread); thread = NULL; break; case BINDER_VERSION: { @@ -3385,8 +4646,27 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = -EINVAL; goto err; } - if (put_user_preempt_disabled(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) { - ret = -EINVAL; + if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, + &ver->protocol_version)) { + ret = -EINVAL; + goto err; + } + break; + } + case BINDER_GET_NODE_DEBUG_INFO: { + struct binder_node_debug_info info; + + if (copy_from_user(&info, ubuf, sizeof(info))) { + ret = -EFAULT; + goto err; + } + + ret = binder_ioctl_get_node_debug_info(proc, &info); + if (ret < 0) + goto err; + + if (copy_to_user(ubuf, &info, sizeof(info))) { + ret = -EFAULT; goto err; } break; @@ -3398,8 +4678,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = 0; err: if (thread) - thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; - binder_unlock(context, __func__); + thread->looper_need_return = false; wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); if (ret && ret != -ERESTARTSYS) pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); @@ -3428,8 +4707,7 @@ static void binder_vma_close(struct vm_area_struct *vma) proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); - proc->vma = NULL; - proc->vma_vm_mm = NULL; + binder_alloc_vma_close(&proc->alloc); binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); } @@ -3447,11 +4725,8 @@ static const struct vm_operations_struct binder_vm_ops = { static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; - - struct vm_struct *area; struct binder_proc *proc = filp->private_data; const char *failure_string; - struct binder_buffer *buffer; if (proc->tsk != current->group_leader) return -EINVAL; @@ -3460,8 +4735,8 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_end = vma->vm_start + SZ_4M; binder_debug(BINDER_DEBUG_OPEN_CLOSE, - "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n", - proc->pid, vma->vm_start, vma->vm_end, + "%s: %d %lx-%lx (%ld K) vma %lx pagep %lx\n", + __func__, proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); @@ -3471,77 +4746,15 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) goto err_bad_arg; } vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; - - mutex_lock(&proc->context->binder_mmap_lock); - if (proc->buffer) { - ret = -EBUSY; - failure_string = "already mapped"; - goto err_already_mapped; - } - - area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); - if (area == NULL) { - ret = -ENOMEM; - failure_string = "get_vm_area"; - goto err_get_vm_area_failed; - } - proc->buffer = area->addr; - proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; - mutex_unlock(&proc->context->binder_mmap_lock); - -#ifdef CONFIG_CPU_CACHE_VIPT - if (cache_is_vipt_aliasing()) { - while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) { - pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer); - vma->vm_start += PAGE_SIZE; - } - } -#endif - proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL); - if (proc->pages == NULL) { - ret = -ENOMEM; - failure_string = "alloc page array"; - goto err_alloc_pages_failed; - } - proc->buffer_size = vma->vm_end - vma->vm_start; - vma->vm_ops = &binder_vm_ops; vma->vm_private_data = proc; - /* binder_update_page_range assumes preemption is disabled */ - preempt_disable(); - ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma); - preempt_enable_no_resched(); - if (ret) { - ret = -ENOMEM; - failure_string = "alloc small buf"; - goto err_alloc_small_buf_failed; - } - buffer = proc->buffer; - INIT_LIST_HEAD(&proc->buffers); - list_add(&buffer->entry, &proc->buffers); - buffer->free = 1; - binder_insert_free_buffer(proc, buffer); - proc->free_async_space = proc->buffer_size / 2; - barrier(); + ret = binder_alloc_mmap_handler(&proc->alloc, vma); + if (ret) + return ret; proc->files = get_files_struct(current); - proc->vma = vma; - proc->vma_vm_mm = vma->vm_mm; - - /*pr_info("binder_mmap: %d %lx-%lx maps %p\n", - proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/ return 0; -err_alloc_small_buf_failed: - kfree(proc->pages); - proc->pages = NULL; -err_alloc_pages_failed: - mutex_lock(&proc->context->binder_mmap_lock); - vfree(proc->buffer); - proc->buffer = NULL; -err_get_vm_area_failed: -err_already_mapped: - mutex_unlock(&proc->context->binder_mmap_lock); err_bad_arg: pr_err("binder_mmap: %d %lx-%lx %s failed %d\n", proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); @@ -3559,24 +4772,33 @@ static int binder_open(struct inode *nodp, struct file *filp) proc = kzalloc(sizeof(*proc), GFP_KERNEL); if (proc == NULL) return -ENOMEM; + spin_lock_init(&proc->inner_lock); + spin_lock_init(&proc->outer_lock); get_task_struct(current->group_leader); proc->tsk = current->group_leader; INIT_LIST_HEAD(&proc->todo); - init_waitqueue_head(&proc->wait); - proc->default_priority = task_nice(current); + if (binder_supported_policy(current->policy)) { + proc->default_priority.sched_policy = current->policy; + proc->default_priority.prio = current->normal_prio; + } else { + proc->default_priority.sched_policy = SCHED_NORMAL; + proc->default_priority.prio = NICE_TO_PRIO(0); + } + binder_dev = container_of(filp->private_data, struct binder_device, miscdev); proc->context = &binder_dev->context; - - binder_lock(proc->context, __func__); + binder_alloc_init(&proc->alloc); binder_stats_created(BINDER_STAT_PROC); - hlist_add_head(&proc->proc_node, &proc->context->binder_procs); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); + INIT_LIST_HEAD(&proc->waiting_threads); filp->private_data = proc; - binder_unlock(proc->context, __func__); + mutex_lock(&binder_procs_lock); + hlist_add_head(&proc->proc_node, &binder_procs); + mutex_unlock(&binder_procs_lock); if (binder_debugfs_dir_entry_proc) { char strbuf[11]; @@ -3612,16 +4834,17 @@ static void binder_deferred_flush(struct binder_proc *proc) struct rb_node *n; int wake_count = 0; + binder_inner_proc_lock(proc); for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) { struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node); - thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; + thread->looper_need_return = true; if (thread->looper & BINDER_LOOPER_STATE_WAITING) { wake_up_interruptible(&thread->wait); wake_count++; } } - wake_up_interruptible_all(&proc->wait); + binder_inner_proc_unlock(proc); binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_flush: %d woke %d threads\n", proc->pid, @@ -3641,15 +4864,22 @@ static int binder_release(struct inode *nodp, struct file *filp) static int binder_node_release(struct binder_node *node, int refs) { struct binder_ref *ref; - struct binder_context *context = node->proc->context; int death = 0; + struct binder_proc *proc = node->proc; - list_del_init(&node->work.entry); - binder_release_work(&node->async_todo); + binder_release_work(proc, &node->async_todo); - if (hlist_empty(&node->refs)) { - kfree(node); - binder_stats_deleted(BINDER_STAT_NODE); + binder_node_lock(node); + binder_inner_proc_lock(proc); + binder_dequeue_work_ilocked(&node->work); + /* + * The caller must have taken a temporary ref on the node, + */ + BUG_ON(!node->tmp_refs); + if (hlist_empty(&node->refs) && node->tmp_refs == 1) { + binder_inner_proc_unlock(proc); + binder_node_unlock(node); + binder_free_node(node); return refs; } @@ -3657,45 +4887,58 @@ static int binder_node_release(struct binder_node *node, int refs) node->proc = NULL; node->local_strong_refs = 0; node->local_weak_refs = 0; - hlist_add_head(&node->dead_node, &context->binder_dead_nodes); + binder_inner_proc_unlock(proc); + + spin_lock(&binder_dead_nodes_lock); + hlist_add_head(&node->dead_node, &binder_dead_nodes); + spin_unlock(&binder_dead_nodes_lock); hlist_for_each_entry(ref, &node->refs, node_entry) { refs++; - - if (!ref->death) + /* + * Need the node lock to synchronize + * with new notification requests and the + * inner lock to synchronize with queued + * death notifications. + */ + binder_inner_proc_lock(ref->proc); + if (!ref->death) { + binder_inner_proc_unlock(ref->proc); continue; + } death++; - if (list_empty(&ref->death->work.entry)) { - ref->death->work.type = BINDER_WORK_DEAD_BINDER; - list_add_tail(&ref->death->work.entry, - &ref->proc->todo); - wake_up_interruptible(&ref->proc->wait); - } else - BUG(); + BUG_ON(!list_empty(&ref->death->work.entry)); + ref->death->work.type = BINDER_WORK_DEAD_BINDER; + binder_enqueue_work_ilocked(&ref->death->work, + &ref->proc->todo); + binder_wakeup_proc_ilocked(ref->proc); + binder_inner_proc_unlock(ref->proc); } binder_debug(BINDER_DEBUG_DEAD_BINDER, "node %d now dead, refs %d, death %d\n", node->debug_id, refs, death); + binder_node_unlock(node); + binder_put_node(node); return refs; } static void binder_deferred_release(struct binder_proc *proc) { - struct binder_transaction *t; struct binder_context *context = proc->context; struct rb_node *n; - int threads, nodes, incoming_refs, outgoing_refs, buffers, - active_transactions, page_count; + int threads, nodes, incoming_refs, outgoing_refs, active_transactions; - BUG_ON(proc->vma); BUG_ON(proc->files); + mutex_lock(&binder_procs_lock); hlist_del(&proc->proc_node); + mutex_unlock(&binder_procs_lock); + mutex_lock(&context->context_mgr_node_lock); if (context->binder_context_mgr_node && context->binder_context_mgr_node->proc == proc) { binder_debug(BINDER_DEBUG_DEAD_BINDER, @@ -3703,15 +4946,25 @@ static void binder_deferred_release(struct binder_proc *proc) __func__, proc->pid); context->binder_context_mgr_node = NULL; } + mutex_unlock(&context->context_mgr_node_lock); + binder_inner_proc_lock(proc); + /* + * Make sure proc stays alive after we + * remove all the threads + */ + proc->tmp_ref++; + proc->is_dead = true; threads = 0; active_transactions = 0; while ((n = rb_first(&proc->threads))) { struct binder_thread *thread; thread = rb_entry(n, struct binder_thread, rb_node); + binder_inner_proc_unlock(proc); threads++; - active_transactions += binder_free_thread(proc, thread); + active_transactions += binder_thread_release(proc, thread); + binder_inner_proc_lock(proc); } nodes = 0; @@ -3721,96 +4974,55 @@ static void binder_deferred_release(struct binder_proc *proc) node = rb_entry(n, struct binder_node, rb_node); nodes++; + /* + * take a temporary ref on the node before + * calling binder_node_release() which will either + * kfree() the node or call binder_put_node() + */ + binder_inc_node_tmpref_ilocked(node); rb_erase(&node->rb_node, &proc->nodes); - incoming_refs = binder_node_release(node, - incoming_refs); + binder_inner_proc_unlock(proc); + incoming_refs = binder_node_release(node, incoming_refs); + binder_inner_proc_lock(proc); } + binder_inner_proc_unlock(proc); outgoing_refs = 0; + binder_proc_lock(proc); while ((n = rb_first(&proc->refs_by_desc))) { struct binder_ref *ref; ref = rb_entry(n, struct binder_ref, rb_node_desc); outgoing_refs++; - binder_delete_ref(ref); - } - - binder_release_work(&proc->todo); - binder_release_work(&proc->delivered_death); - - buffers = 0; - while ((n = rb_first(&proc->allocated_buffers))) { - struct binder_buffer *buffer; - - buffer = rb_entry(n, struct binder_buffer, rb_node); - - t = buffer->transaction; - if (t) { - t->buffer = NULL; - buffer->transaction = NULL; - pr_err("release proc %d, transaction %d, not freed\n", - proc->pid, t->debug_id); - /*BUG();*/ - } - - binder_free_buf(proc, buffer); - buffers++; - } - - binder_stats_deleted(BINDER_STAT_PROC); - - page_count = 0; - if (proc->pages) { - int i; - - for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) { - void *page_addr; - - if (!proc->pages[i]) - continue; - - page_addr = proc->buffer + i * PAGE_SIZE; - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%s: %d: page %d at %p not freed\n", - __func__, proc->pid, i, page_addr); - unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); - __free_page(proc->pages[i]); - page_count++; - } - kfree(proc->pages); - preempt_enable_no_resched(); - vfree(proc->buffer); - preempt_disable(); + binder_cleanup_ref_olocked(ref); + binder_proc_unlock(proc); + binder_free_ref(ref); + binder_proc_lock(proc); } + binder_proc_unlock(proc); - put_task_struct(proc->tsk); + binder_release_work(proc, &proc->todo); + binder_release_work(proc, &proc->delivered_death); binder_debug(BINDER_DEBUG_OPEN_CLOSE, - "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d, buffers %d, pages %d\n", + "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n", __func__, proc->pid, threads, nodes, incoming_refs, - outgoing_refs, active_transactions, buffers, page_count); + outgoing_refs, active_transactions); - kfree(proc); + binder_proc_dec_tmpref(proc); } static void binder_deferred_func(struct work_struct *work) { struct binder_proc *proc; struct files_struct *files; - struct binder_context *context = - container_of(work, struct binder_context, deferred_work); int defer; do { - trace_binder_lock(__func__); - mutex_lock(&context->binder_main_lock); - trace_binder_locked(__func__); - - mutex_lock(&context->binder_deferred_lock); - preempt_disable(); - if (!hlist_empty(&context->binder_deferred_list)) { - proc = hlist_entry(context->binder_deferred_list.first, + mutex_lock(&binder_deferred_lock); + if (!hlist_empty(&binder_deferred_list)) { + proc = hlist_entry(binder_deferred_list.first, struct binder_proc, deferred_work_node); hlist_del_init(&proc->deferred_work_node); defer = proc->deferred_work; @@ -3819,7 +5031,7 @@ static void binder_deferred_func(struct work_struct *work) proc = NULL; defer = 0; } - mutex_unlock(&context->binder_deferred_lock); + mutex_unlock(&binder_deferred_lock); files = NULL; if (defer & BINDER_DEFERRED_PUT_FILES) { @@ -3834,63 +5046,71 @@ static void binder_deferred_func(struct work_struct *work) if (defer & BINDER_DEFERRED_RELEASE) binder_deferred_release(proc); /* frees proc */ - trace_binder_unlock(__func__); - mutex_unlock(&context->binder_main_lock); - preempt_enable_no_resched(); if (files) put_files_struct(files); } while (proc); } +static DECLARE_WORK(binder_deferred_work, binder_deferred_func); static void binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer) { - mutex_lock(&proc->context->binder_deferred_lock); + mutex_lock(&binder_deferred_lock); proc->deferred_work |= defer; if (hlist_unhashed(&proc->deferred_work_node)) { hlist_add_head(&proc->deferred_work_node, - &proc->context->binder_deferred_list); - queue_work(proc->context->binder_deferred_workqueue, - &proc->context->deferred_work); + &binder_deferred_list); + queue_work(binder_deferred_workqueue, &binder_deferred_work); } - mutex_unlock(&proc->context->binder_deferred_lock); + mutex_unlock(&binder_deferred_lock); } -static void print_binder_transaction(struct seq_file *m, const char *prefix, - struct binder_transaction *t) +static void print_binder_transaction_ilocked(struct seq_file *m, + struct binder_proc *proc, + const char *prefix, + struct binder_transaction *t) { + struct binder_proc *to_proc; + struct binder_buffer *buffer = t->buffer; + + spin_lock(&t->lock); + to_proc = t->to_proc; seq_printf(m, - "%s %d: %p from %d:%d to %d:%d code %x flags %x pri %ld r%d", + "%s %d: %p from %d:%d to %d:%d code %x flags %x pri %d:%d r%d", prefix, t->debug_id, t, t->from ? t->from->proc->pid : 0, t->from ? t->from->pid : 0, - t->to_proc ? t->to_proc->pid : 0, + to_proc ? to_proc->pid : 0, t->to_thread ? t->to_thread->pid : 0, - t->code, t->flags, t->priority, t->need_reply); - if (t->buffer == NULL) { + t->code, t->flags, t->priority.sched_policy, + t->priority.prio, t->need_reply); + spin_unlock(&t->lock); + + if (proc != to_proc) { + /* + * Can only safely deref buffer if we are holding the + * correct proc inner lock for this node + */ + seq_puts(m, "\n"); + return; + } + + if (buffer == NULL) { seq_puts(m, " buffer free\n"); return; } - if (t->buffer->target_node) - seq_printf(m, " node %d", - t->buffer->target_node->debug_id); + if (buffer->target_node) + seq_printf(m, " node %d", buffer->target_node->debug_id); seq_printf(m, " size %zd:%zd data %p\n", - t->buffer->data_size, t->buffer->offsets_size, - t->buffer->data); -} - -static void print_binder_buffer(struct seq_file *m, const char *prefix, - struct binder_buffer *buffer) -{ - seq_printf(m, "%s %d: %p size %zd:%zd %s\n", - prefix, buffer->debug_id, buffer->data, buffer->data_size, buffer->offsets_size, - buffer->transaction ? "active" : "delivered"); + buffer->data); } -static void print_binder_work(struct seq_file *m, const char *prefix, - const char *transaction_prefix, - struct binder_work *w) +static void print_binder_work_ilocked(struct seq_file *m, + struct binder_proc *proc, + const char *prefix, + const char *transaction_prefix, + struct binder_work *w) { struct binder_node *node; struct binder_transaction *t; @@ -3898,8 +5118,16 @@ static void print_binder_work(struct seq_file *m, const char *prefix, switch (w->type) { case BINDER_WORK_TRANSACTION: t = container_of(w, struct binder_transaction, work); - print_binder_transaction(m, transaction_prefix, t); + print_binder_transaction_ilocked( + m, proc, transaction_prefix, t); break; + case BINDER_WORK_RETURN_ERROR: { + struct binder_error *e = container_of( + w, struct binder_error, work); + + seq_printf(m, "%stransaction error: %u\n", + prefix, e->cmd); + } break; case BINDER_WORK_TRANSACTION_COMPLETE: seq_printf(m, "%stransaction complete\n", prefix); break; @@ -3924,40 +5152,46 @@ static void print_binder_work(struct seq_file *m, const char *prefix, } } -static void print_binder_thread(struct seq_file *m, - struct binder_thread *thread, - int print_always) +static void print_binder_thread_ilocked(struct seq_file *m, + struct binder_thread *thread, + int print_always) { struct binder_transaction *t; struct binder_work *w; size_t start_pos = m->count; size_t header_pos; - seq_printf(m, " thread %d: l %02x\n", thread->pid, thread->looper); + seq_printf(m, " thread %d: l %02x need_return %d tr %d\n", + thread->pid, thread->looper, + thread->looper_need_return, + atomic_read(&thread->tmp_ref)); header_pos = m->count; t = thread->transaction_stack; while (t) { if (t->from == thread) { - print_binder_transaction(m, - " outgoing transaction", t); + print_binder_transaction_ilocked(m, thread->proc, + " outgoing transaction", t); t = t->from_parent; } else if (t->to_thread == thread) { - print_binder_transaction(m, + print_binder_transaction_ilocked(m, thread->proc, " incoming transaction", t); t = t->to_parent; } else { - print_binder_transaction(m, " bad transaction", t); + print_binder_transaction_ilocked(m, thread->proc, + " bad transaction", t); t = NULL; } } list_for_each_entry(w, &thread->todo, entry) { - print_binder_work(m, " ", " pending transaction", w); + print_binder_work_ilocked(m, thread->proc, " ", + " pending transaction", w); } if (!print_always && m->count == header_pos) m->count = start_pos; } -static void print_binder_node(struct seq_file *m, struct binder_node *node) +static void print_binder_node_nilocked(struct seq_file *m, + struct binder_node *node) { struct binder_ref *ref; struct binder_work *w; @@ -3967,27 +5201,35 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node) hlist_for_each_entry(ref, &node->refs, node_entry) count++; - seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d", + seq_printf(m, " node %d: u%016llx c%016llx pri %d:%d hs %d hw %d ls %d lw %d is %d iw %d tr %d", node->debug_id, (u64)node->ptr, (u64)node->cookie, + node->sched_policy, node->min_priority, node->has_strong_ref, node->has_weak_ref, node->local_strong_refs, node->local_weak_refs, - node->internal_strong_refs, count); + node->internal_strong_refs, count, node->tmp_refs); if (count) { seq_puts(m, " proc"); hlist_for_each_entry(ref, &node->refs, node_entry) seq_printf(m, " %d", ref->proc->pid); } seq_puts(m, "\n"); - list_for_each_entry(w, &node->async_todo, entry) - print_binder_work(m, " ", - " pending async transaction", w); + if (node->proc) { + list_for_each_entry(w, &node->async_todo, entry) + print_binder_work_ilocked(m, node->proc, " ", + " pending async transaction", w); + } } -static void print_binder_ref(struct seq_file *m, struct binder_ref *ref) +static void print_binder_ref_olocked(struct seq_file *m, + struct binder_ref *ref) { + binder_node_lock(ref->node); seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n", - ref->debug_id, ref->desc, ref->node->proc ? "" : "dead ", - ref->node->debug_id, ref->strong, ref->weak, ref->death); + ref->data.debug_id, ref->data.desc, + ref->node->proc ? "" : "dead ", + ref->node->debug_id, ref->data.strong, + ref->data.weak, ref->death); + binder_node_unlock(ref->node); } static void print_binder_proc(struct seq_file *m, @@ -3997,36 +5239,60 @@ static void print_binder_proc(struct seq_file *m, struct rb_node *n; size_t start_pos = m->count; size_t header_pos; + struct binder_node *last_node = NULL; seq_printf(m, "proc %d\n", proc->pid); seq_printf(m, "context %s\n", proc->context->name); header_pos = m->count; + binder_inner_proc_lock(proc); for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) - print_binder_thread(m, rb_entry(n, struct binder_thread, + print_binder_thread_ilocked(m, rb_entry(n, struct binder_thread, rb_node), print_all); + for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) { struct binder_node *node = rb_entry(n, struct binder_node, rb_node); - if (print_all || node->has_async_transaction) - print_binder_node(m, node); - } + /* + * take a temporary reference on the node so it + * survives and isn't removed from the tree + * while we print it. + */ + binder_inc_node_tmpref_ilocked(node); + /* Need to drop inner lock to take node lock */ + binder_inner_proc_unlock(proc); + if (last_node) + binder_put_node(last_node); + binder_node_inner_lock(node); + print_binder_node_nilocked(m, node); + binder_node_inner_unlock(node); + last_node = node; + binder_inner_proc_lock(proc); + } + binder_inner_proc_unlock(proc); + if (last_node) + binder_put_node(last_node); + if (print_all) { + binder_proc_lock(proc); for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) - print_binder_ref(m, rb_entry(n, struct binder_ref, - rb_node_desc)); + print_binder_ref_olocked(m, rb_entry(n, + struct binder_ref, + rb_node_desc)); + binder_proc_unlock(proc); } - for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n)) - print_binder_buffer(m, " buffer", - rb_entry(n, struct binder_buffer, rb_node)); + binder_alloc_print_allocated(m, &proc->alloc); + binder_inner_proc_lock(proc); list_for_each_entry(w, &proc->todo, entry) - print_binder_work(m, " ", " pending transaction", w); + print_binder_work_ilocked(m, proc, " ", + " pending transaction", w); list_for_each_entry(w, &proc->delivered_death, entry) { seq_puts(m, " has delivered dead binder\n"); break; } + binder_inner_proc_unlock(proc); if (!print_all && m->count == header_pos) m->count = start_pos; } @@ -4084,54 +5350,45 @@ static const char * const binder_objstat_strings[] = { "transaction_complete" }; -static void add_binder_stats(struct binder_stats *from, struct binder_stats *to) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(to->bc); i++) - to->bc[i] += from->bc[i]; - - for (i = 0; i < ARRAY_SIZE(to->br); i++) - to->br[i] += from->br[i]; -} - static void print_binder_stats(struct seq_file *m, const char *prefix, - struct binder_stats *stats, - struct binder_obj_stats *obj_stats) + struct binder_stats *stats) { int i; BUILD_BUG_ON(ARRAY_SIZE(stats->bc) != ARRAY_SIZE(binder_command_strings)); for (i = 0; i < ARRAY_SIZE(stats->bc); i++) { - if (stats->bc[i]) + int temp = atomic_read(&stats->bc[i]); + + if (temp) seq_printf(m, "%s%s: %d\n", prefix, - binder_command_strings[i], stats->bc[i]); + binder_command_strings[i], temp); } BUILD_BUG_ON(ARRAY_SIZE(stats->br) != ARRAY_SIZE(binder_return_strings)); for (i = 0; i < ARRAY_SIZE(stats->br); i++) { - if (stats->br[i]) + int temp = atomic_read(&stats->br[i]); + + if (temp) seq_printf(m, "%s%s: %d\n", prefix, - binder_return_strings[i], stats->br[i]); + binder_return_strings[i], temp); } - if (!obj_stats) - return; - - BUILD_BUG_ON(ARRAY_SIZE(obj_stats->obj_created) != + BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) != ARRAY_SIZE(binder_objstat_strings)); - BUILD_BUG_ON(ARRAY_SIZE(obj_stats->obj_created) != - ARRAY_SIZE(obj_stats->obj_deleted)); - for (i = 0; i < ARRAY_SIZE(obj_stats->obj_created); i++) { - int obj_created = atomic_read(&obj_stats->obj_created[i]); - int obj_deleted = atomic_read(&obj_stats->obj_deleted[i]); - - if (obj_created || obj_deleted) - seq_printf(m, "%s%s: active %d total %d\n", prefix, - binder_objstat_strings[i], - obj_created - obj_deleted, obj_created); + BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) != + ARRAY_SIZE(stats->obj_deleted)); + for (i = 0; i < ARRAY_SIZE(stats->obj_created); i++) { + int created = atomic_read(&stats->obj_created[i]); + int deleted = atomic_read(&stats->obj_deleted[i]); + + if (created || deleted) + seq_printf(m, "%s%s: active %d total %d\n", + prefix, + binder_objstat_strings[i], + created - deleted, + created); } } @@ -4139,226 +5396,193 @@ static void print_binder_proc_stats(struct seq_file *m, struct binder_proc *proc) { struct binder_work *w; + struct binder_thread *thread; struct rb_node *n; - int count, strong, weak; + int count, strong, weak, ready_threads; + size_t free_async_space = + binder_alloc_get_free_async_space(&proc->alloc); seq_printf(m, "proc %d\n", proc->pid); seq_printf(m, "context %s\n", proc->context->name); count = 0; + ready_threads = 0; + binder_inner_proc_lock(proc); for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) count++; + + list_for_each_entry(thread, &proc->waiting_threads, waiting_thread_node) + ready_threads++; + seq_printf(m, " threads: %d\n", count); seq_printf(m, " requested threads: %d+%d/%d\n" " ready threads %d\n" " free async space %zd\n", proc->requested_threads, proc->requested_threads_started, proc->max_threads, - proc->ready_threads, proc->free_async_space); + ready_threads, + free_async_space); count = 0; for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) count++; + binder_inner_proc_unlock(proc); seq_printf(m, " nodes: %d\n", count); count = 0; strong = 0; weak = 0; + binder_proc_lock(proc); for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { struct binder_ref *ref = rb_entry(n, struct binder_ref, rb_node_desc); count++; - strong += ref->strong; - weak += ref->weak; + strong += ref->data.strong; + weak += ref->data.weak; } + binder_proc_unlock(proc); seq_printf(m, " refs: %d s %d w %d\n", count, strong, weak); - count = 0; - for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n)) - count++; + count = binder_alloc_get_allocated_count(&proc->alloc); seq_printf(m, " buffers: %d\n", count); count = 0; + binder_inner_proc_lock(proc); list_for_each_entry(w, &proc->todo, entry) { - switch (w->type) { - case BINDER_WORK_TRANSACTION: + if (w->type == BINDER_WORK_TRANSACTION) count++; - break; - default: - break; - } } + binder_inner_proc_unlock(proc); seq_printf(m, " pending transactions: %d\n", count); - print_binder_stats(m, " ", &proc->stats, NULL); + print_binder_stats(m, " ", &proc->stats); } static int binder_state_show(struct seq_file *m, void *unused) { - struct binder_device *device; - struct binder_context *context; struct binder_proc *proc; struct binder_node *node; - int do_lock = !binder_debug_no_lock; - bool wrote_dead_nodes_header = false; + struct binder_node *last_node = NULL; seq_puts(m, "binder state:\n"); - hlist_for_each_entry(device, &binder_devices, hlist) { - context = &device->context; - if (do_lock) - binder_lock(context, __func__); - if (!wrote_dead_nodes_header && - !hlist_empty(&context->binder_dead_nodes)) { - seq_puts(m, "dead nodes:\n"); - wrote_dead_nodes_header = true; - } - hlist_for_each_entry(node, &context->binder_dead_nodes, - dead_node) - print_binder_node(m, node); - - if (do_lock) - binder_unlock(context, __func__); - } - - hlist_for_each_entry(device, &binder_devices, hlist) { - context = &device->context; - if (do_lock) - binder_lock(context, __func__); + spin_lock(&binder_dead_nodes_lock); + if (!hlist_empty(&binder_dead_nodes)) + seq_puts(m, "dead nodes:\n"); + hlist_for_each_entry(node, &binder_dead_nodes, dead_node) { + /* + * take a temporary reference on the node so it + * survives and isn't removed from the list + * while we print it. + */ + node->tmp_refs++; + spin_unlock(&binder_dead_nodes_lock); + if (last_node) + binder_put_node(last_node); + binder_node_lock(node); + print_binder_node_nilocked(m, node); + binder_node_unlock(node); + last_node = node; + spin_lock(&binder_dead_nodes_lock); + } + spin_unlock(&binder_dead_nodes_lock); + if (last_node) + binder_put_node(last_node); + + mutex_lock(&binder_procs_lock); + hlist_for_each_entry(proc, &binder_procs, proc_node) + print_binder_proc(m, proc, 1); + mutex_unlock(&binder_procs_lock); - hlist_for_each_entry(proc, &context->binder_procs, proc_node) - print_binder_proc(m, proc, 1); - if (do_lock) - binder_unlock(context, __func__); - } return 0; } static int binder_stats_show(struct seq_file *m, void *unused) { - struct binder_device *device; - struct binder_context *context; struct binder_proc *proc; - struct binder_stats total_binder_stats; - int do_lock = !binder_debug_no_lock; - - memset(&total_binder_stats, 0, sizeof(struct binder_stats)); - - hlist_for_each_entry(device, &binder_devices, hlist) { - context = &device->context; - if (do_lock) - binder_lock(context, __func__); - - add_binder_stats(&context->binder_stats, &total_binder_stats); - - if (do_lock) - binder_unlock(context, __func__); - } seq_puts(m, "binder stats:\n"); - print_binder_stats(m, "", &total_binder_stats, &binder_obj_stats); - hlist_for_each_entry(device, &binder_devices, hlist) { - context = &device->context; - if (do_lock) - binder_lock(context, __func__); + print_binder_stats(m, "", &binder_stats); + + mutex_lock(&binder_procs_lock); + hlist_for_each_entry(proc, &binder_procs, proc_node) + print_binder_proc_stats(m, proc); + mutex_unlock(&binder_procs_lock); - hlist_for_each_entry(proc, &context->binder_procs, proc_node) - print_binder_proc_stats(m, proc); - if (do_lock) - binder_unlock(context, __func__); - } return 0; } static int binder_transactions_show(struct seq_file *m, void *unused) { - struct binder_device *device; - struct binder_context *context; struct binder_proc *proc; - int do_lock = !binder_debug_no_lock; seq_puts(m, "binder transactions:\n"); - hlist_for_each_entry(device, &binder_devices, hlist) { - context = &device->context; - if (do_lock) - binder_lock(context, __func__); - - hlist_for_each_entry(proc, &context->binder_procs, proc_node) - print_binder_proc(m, proc, 0); - if (do_lock) - binder_unlock(context, __func__); - } + mutex_lock(&binder_procs_lock); + hlist_for_each_entry(proc, &binder_procs, proc_node) + print_binder_proc(m, proc, 0); + mutex_unlock(&binder_procs_lock); + return 0; } static int binder_proc_show(struct seq_file *m, void *unused) { - struct binder_device *device; - struct binder_context *context; struct binder_proc *itr; int pid = (unsigned long)m->private; - int do_lock = !binder_debug_no_lock; - hlist_for_each_entry(device, &binder_devices, hlist) { - context = &device->context; - if (do_lock) - binder_lock(context, __func__); - - hlist_for_each_entry(itr, &context->binder_procs, proc_node) { - if (itr->pid == pid) { - seq_puts(m, "binder proc state:\n"); - print_binder_proc(m, itr, 1); - } + mutex_lock(&binder_procs_lock); + hlist_for_each_entry(itr, &binder_procs, proc_node) { + if (itr->pid == pid) { + seq_puts(m, "binder proc state:\n"); + print_binder_proc(m, itr, 1); } - if (do_lock) - binder_unlock(context, __func__); } + mutex_unlock(&binder_procs_lock); + return 0; } static void print_binder_transaction_log_entry(struct seq_file *m, struct binder_transaction_log_entry *e) { + int debug_id = READ_ONCE(e->debug_id_done); + /* + * read barrier to guarantee debug_id_done read before + * we print the log values + */ + smp_rmb(); seq_printf(m, - "%d: %s from %d:%d to %d:%d context %s node %d handle %d size %d:%d\n", + "%d: %s from %d:%d to %d:%d context %s node %d handle %d size %d:%d ret %d/%d l=%d", e->debug_id, (e->call_type == 2) ? "reply" : ((e->call_type == 1) ? "async" : "call "), e->from_proc, e->from_thread, e->to_proc, e->to_thread, e->context_name, - e->to_node, e->target_handle, e->data_size, e->offsets_size); -} - -static int print_binder_transaction_log(struct seq_file *m, - struct binder_transaction_log *log) -{ - int i; - if (log->full) { - for (i = log->next; i < ARRAY_SIZE(log->entry); i++) - print_binder_transaction_log_entry(m, &log->entry[i]); - } - for (i = 0; i < log->next; i++) - print_binder_transaction_log_entry(m, &log->entry[i]); - return 0; + e->to_node, e->target_handle, e->data_size, e->offsets_size, + e->return_error, e->return_error_param, + e->return_error_line); + /* + * read-barrier to guarantee read of debug_id_done after + * done printing the fields of the entry + */ + smp_rmb(); + seq_printf(m, debug_id && debug_id == READ_ONCE(e->debug_id_done) ? + "\n" : " (incomplete)\n"); } static int binder_transaction_log_show(struct seq_file *m, void *unused) { - struct binder_device *device; - struct binder_context *context; - - hlist_for_each_entry(device, &binder_devices, hlist) { - context = &device->context; - print_binder_transaction_log(m, &context->transaction_log); - } - return 0; -} + struct binder_transaction_log *log = m->private; + unsigned int log_cur = atomic_read(&log->cur); + unsigned int count; + unsigned int cur; + int i; -static int binder_failed_transaction_log_show(struct seq_file *m, void *unused) -{ - struct binder_device *device; - struct binder_context *context; + count = log_cur + 1; + cur = count < ARRAY_SIZE(log->entry) && !log->full ? + 0 : count % ARRAY_SIZE(log->entry); + if (count > ARRAY_SIZE(log->entry) || log->full) + count = ARRAY_SIZE(log->entry); + for (i = 0; i < count; i++) { + unsigned int index = cur++ % ARRAY_SIZE(log->entry); - hlist_for_each_entry(device, &binder_devices, hlist) { - context = &device->context; - print_binder_transaction_log(m, - &context->transaction_log_failed); + print_binder_transaction_log_entry(m, &log->entry[index]); } return 0; } @@ -4378,20 +5602,11 @@ BINDER_DEBUG_ENTRY(state); BINDER_DEBUG_ENTRY(stats); BINDER_DEBUG_ENTRY(transactions); BINDER_DEBUG_ENTRY(transaction_log); -BINDER_DEBUG_ENTRY(failed_transaction_log); - -static void __init free_binder_device(struct binder_device *device) -{ - if (device->context.binder_deferred_workqueue) - destroy_workqueue(device->context.binder_deferred_workqueue); - kfree(device); -} static int __init init_binder_device(const char *name) { int ret; struct binder_device *binder_device; - struct binder_context *context; binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL); if (!binder_device) @@ -4401,65 +5616,34 @@ static int __init init_binder_device(const char *name) binder_device->miscdev.minor = MISC_DYNAMIC_MINOR; binder_device->miscdev.name = name; - context = &binder_device->context; - context->binder_context_mgr_uid = INVALID_UID; - context->name = name; - - mutex_init(&context->binder_main_lock); - mutex_init(&context->binder_deferred_lock); - mutex_init(&context->binder_mmap_lock); - - context->binder_deferred_workqueue = - create_singlethread_workqueue(name); - - if (!context->binder_deferred_workqueue) { - ret = -ENOMEM; - goto err_create_singlethread_workqueue_failed; - } - - INIT_HLIST_HEAD(&context->binder_procs); - INIT_HLIST_HEAD(&context->binder_dead_nodes); - INIT_HLIST_HEAD(&context->binder_deferred_list); - INIT_WORK(&context->deferred_work, binder_deferred_func); + binder_device->context.binder_context_mgr_uid = INVALID_UID; + binder_device->context.name = name; + mutex_init(&binder_device->context.context_mgr_node_lock); ret = misc_register(&binder_device->miscdev); if (ret < 0) { - goto err_misc_register_failed; + kfree(binder_device); + return ret; } hlist_add_head(&binder_device->hlist, &binder_devices); - return ret; - -err_create_singlethread_workqueue_failed: -err_misc_register_failed: - free_binder_device(binder_device); return ret; } static int __init binder_init(void) { - int ret = 0; + int ret; char *device_name, *device_names; struct binder_device *device; struct hlist_node *tmp; - /* - * Copy the module_parameter string, because we don't want to - * tokenize it in-place. - */ - device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL); - if (!device_names) + atomic_set(&binder_transaction_log.cur, ~0U); + atomic_set(&binder_transaction_log_failed.cur, ~0U); + binder_deferred_workqueue = create_singlethread_workqueue("binder"); + if (!binder_deferred_workqueue) return -ENOMEM; - strcpy(device_names, binder_devices_param); - - while ((device_name = strsep(&device_names, ","))) { - ret = init_binder_device(device_name); - if (ret) - goto err_init_binder_device_failed; - } - binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL); if (binder_debugfs_dir_entry_root) binder_debugfs_dir_entry_proc = debugfs_create_dir("proc", @@ -4484,13 +5668,30 @@ static int __init binder_init(void) debugfs_create_file("transaction_log", S_IRUGO, binder_debugfs_dir_entry_root, - NULL, + &binder_transaction_log, &binder_transaction_log_fops); debugfs_create_file("failed_transaction_log", S_IRUGO, binder_debugfs_dir_entry_root, - NULL, - &binder_failed_transaction_log_fops); + &binder_transaction_log_failed, + &binder_transaction_log_fops); + } + + /* + * Copy the module_parameter string, because we don't want to + * tokenize it in-place. + */ + device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL); + if (!device_names) { + ret = -ENOMEM; + goto err_alloc_device_names_failed; + } + strcpy(device_names, binder_devices_param); + + while ((device_name = strsep(&device_names, ","))) { + ret = init_binder_device(device_name); + if (ret) + goto err_init_binder_device_failed; } return ret; @@ -4499,8 +5700,12 @@ err_init_binder_device_failed: hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) { misc_deregister(&device->miscdev); hlist_del(&device->hlist); - free_binder_device(device); + kfree(device); } +err_alloc_device_names_failed: + debugfs_remove_recursive(binder_debugfs_dir_entry_root); + + destroy_workqueue(binder_deferred_workqueue); return ret; } diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c new file mode 100644 index 000000000000..aabfebac6e57 --- /dev/null +++ b/drivers/android/binder_alloc.c @@ -0,0 +1,802 @@ +/* binder_alloc.c + * + * Android IPC Subsystem + * + * Copyright (C) 2007-2017 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <asm/cacheflush.h> +#include <linux/list.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/rtmutex.h> +#include <linux/rbtree.h> +#include <linux/seq_file.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include "binder_alloc.h" +#include "binder_trace.h" + +static DEFINE_MUTEX(binder_alloc_mmap_lock); + +enum { + BINDER_DEBUG_OPEN_CLOSE = 1U << 1, + BINDER_DEBUG_BUFFER_ALLOC = 1U << 2, + BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 3, +}; +static uint32_t binder_alloc_debug_mask; + +module_param_named(debug_mask, binder_alloc_debug_mask, + uint, S_IWUSR | S_IRUGO); + +#define binder_alloc_debug(mask, x...) \ + do { \ + if (binder_alloc_debug_mask & mask) \ + pr_info(x); \ + } while (0) + +static size_t binder_alloc_buffer_size(struct binder_alloc *alloc, + struct binder_buffer *buffer) +{ + if (list_is_last(&buffer->entry, &alloc->buffers)) + return alloc->buffer + + alloc->buffer_size - (void *)buffer->data; + return (size_t)list_entry(buffer->entry.next, + struct binder_buffer, entry) - (size_t)buffer->data; +} + +static void binder_insert_free_buffer(struct binder_alloc *alloc, + struct binder_buffer *new_buffer) +{ + struct rb_node **p = &alloc->free_buffers.rb_node; + struct rb_node *parent = NULL; + struct binder_buffer *buffer; + size_t buffer_size; + size_t new_buffer_size; + + BUG_ON(!new_buffer->free); + + new_buffer_size = binder_alloc_buffer_size(alloc, new_buffer); + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: add free buffer, size %zd, at %pK\n", + alloc->pid, new_buffer_size, new_buffer); + + while (*p) { + parent = *p; + buffer = rb_entry(parent, struct binder_buffer, rb_node); + BUG_ON(!buffer->free); + + buffer_size = binder_alloc_buffer_size(alloc, buffer); + + if (new_buffer_size < buffer_size) + p = &parent->rb_left; + else + p = &parent->rb_right; + } + rb_link_node(&new_buffer->rb_node, parent, p); + rb_insert_color(&new_buffer->rb_node, &alloc->free_buffers); +} + +static void binder_insert_allocated_buffer_locked( + struct binder_alloc *alloc, struct binder_buffer *new_buffer) +{ + struct rb_node **p = &alloc->allocated_buffers.rb_node; + struct rb_node *parent = NULL; + struct binder_buffer *buffer; + + BUG_ON(new_buffer->free); + + while (*p) { + parent = *p; + buffer = rb_entry(parent, struct binder_buffer, rb_node); + BUG_ON(buffer->free); + + if (new_buffer < buffer) + p = &parent->rb_left; + else if (new_buffer > buffer) + p = &parent->rb_right; + else + BUG(); + } + rb_link_node(&new_buffer->rb_node, parent, p); + rb_insert_color(&new_buffer->rb_node, &alloc->allocated_buffers); +} + +static struct binder_buffer *binder_alloc_prepare_to_free_locked( + struct binder_alloc *alloc, + uintptr_t user_ptr) +{ + struct rb_node *n = alloc->allocated_buffers.rb_node; + struct binder_buffer *buffer; + struct binder_buffer *kern_ptr; + + kern_ptr = (struct binder_buffer *)(user_ptr - alloc->user_buffer_offset + - offsetof(struct binder_buffer, data)); + + while (n) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + BUG_ON(buffer->free); + + if (kern_ptr < buffer) + n = n->rb_left; + else if (kern_ptr > buffer) + n = n->rb_right; + else { + /* + * Guard against user threads attempting to + * free the buffer twice + */ + if (buffer->free_in_progress) { + pr_err("%d:%d FREE_BUFFER u%016llx user freed buffer twice\n", + alloc->pid, current->pid, (u64)user_ptr); + return NULL; + } + buffer->free_in_progress = 1; + return buffer; + } + } + return NULL; +} + +/** + * binder_alloc_buffer_lookup() - get buffer given user ptr + * @alloc: binder_alloc for this proc + * @user_ptr: User pointer to buffer data + * + * Validate userspace pointer to buffer data and return buffer corresponding to + * that user pointer. Search the rb tree for buffer that matches user data + * pointer. + * + * Return: Pointer to buffer or NULL + */ +struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc, + uintptr_t user_ptr) +{ + struct binder_buffer *buffer; + + mutex_lock(&alloc->mutex); + buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr); + mutex_unlock(&alloc->mutex); + return buffer; +} + +static int binder_update_page_range(struct binder_alloc *alloc, int allocate, + void *start, void *end, + struct vm_area_struct *vma) +{ + void *page_addr; + unsigned long user_page_addr; + struct page **page; + struct mm_struct *mm; + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: %s pages %pK-%pK\n", alloc->pid, + allocate ? "allocate" : "free", start, end); + + if (end <= start) + return 0; + + trace_binder_update_page_range(alloc, allocate, start, end); + + if (vma) + mm = NULL; + else + mm = get_task_mm(alloc->tsk); + + if (mm) { + down_write(&mm->mmap_sem); + vma = alloc->vma; + if (vma && mm != alloc->vma_vm_mm) { + pr_err("%d: vma mm and task mm mismatch\n", + alloc->pid); + vma = NULL; + } + } + + if (allocate == 0) + goto free_range; + + if (vma == NULL) { + pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n", + alloc->pid); + goto err_no_vma; + } + + for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { + int ret; + + page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE]; + + BUG_ON(*page); + *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); + if (*page == NULL) { + pr_err("%d: binder_alloc_buf failed for page at %pK\n", + alloc->pid, page_addr); + goto err_alloc_page_failed; + } + ret = map_kernel_range_noflush((unsigned long)page_addr, + PAGE_SIZE, PAGE_KERNEL, page); + flush_cache_vmap((unsigned long)page_addr, + (unsigned long)page_addr + PAGE_SIZE); + if (ret != 1) { + pr_err("%d: binder_alloc_buf failed to map page at %pK in kernel\n", + alloc->pid, page_addr); + goto err_map_kernel_failed; + } + user_page_addr = + (uintptr_t)page_addr + alloc->user_buffer_offset; + ret = vm_insert_page(vma, user_page_addr, page[0]); + if (ret) { + pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n", + alloc->pid, user_page_addr); + goto err_vm_insert_page_failed; + } + /* vm_insert_page does not seem to increment the refcount */ + } + if (mm) { + up_write(&mm->mmap_sem); + mmput(mm); + } + return 0; + +free_range: + for (page_addr = end - PAGE_SIZE; page_addr >= start; + page_addr -= PAGE_SIZE) { + page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE]; + if (vma) + zap_page_range(vma, (uintptr_t)page_addr + + alloc->user_buffer_offset, PAGE_SIZE, NULL); +err_vm_insert_page_failed: + unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); +err_map_kernel_failed: + __free_page(*page); + *page = NULL; +err_alloc_page_failed: + ; + } +err_no_vma: + if (mm) { + up_write(&mm->mmap_sem); + mmput(mm); + } + return vma ? -ENOMEM : -ESRCH; +} + +struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc, + size_t data_size, + size_t offsets_size, + size_t extra_buffers_size, + int is_async) +{ + struct rb_node *n = alloc->free_buffers.rb_node; + struct binder_buffer *buffer; + size_t buffer_size; + struct rb_node *best_fit = NULL; + void *has_page_addr; + void *end_page_addr; + size_t size, data_offsets_size; + int ret; + + if (alloc->vma == NULL) { + pr_err("%d: binder_alloc_buf, no vma\n", + alloc->pid); + return ERR_PTR(-ESRCH); + } + + data_offsets_size = ALIGN(data_size, sizeof(void *)) + + ALIGN(offsets_size, sizeof(void *)); + + if (data_offsets_size < data_size || data_offsets_size < offsets_size) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: got transaction with invalid size %zd-%zd\n", + alloc->pid, data_size, offsets_size); + return ERR_PTR(-EINVAL); + } + size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *)); + if (size < data_offsets_size || size < extra_buffers_size) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: got transaction with invalid extra_buffers_size %zd\n", + alloc->pid, extra_buffers_size); + return ERR_PTR(-EINVAL); + } + if (is_async && + alloc->free_async_space < size + sizeof(struct binder_buffer)) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: binder_alloc_buf size %zd failed, no async space left\n", + alloc->pid, size); + return ERR_PTR(-ENOSPC); + } + + while (n) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + BUG_ON(!buffer->free); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + + if (size < buffer_size) { + best_fit = n; + n = n->rb_left; + } else if (size > buffer_size) + n = n->rb_right; + else { + best_fit = n; + break; + } + } + if (best_fit == NULL) { + size_t allocated_buffers = 0; + size_t largest_alloc_size = 0; + size_t total_alloc_size = 0; + size_t free_buffers = 0; + size_t largest_free_size = 0; + size_t total_free_size = 0; + + for (n = rb_first(&alloc->allocated_buffers); n != NULL; + n = rb_next(n)) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + allocated_buffers++; + total_alloc_size += buffer_size; + if (buffer_size > largest_alloc_size) + largest_alloc_size = buffer_size; + } + for (n = rb_first(&alloc->free_buffers); n != NULL; + n = rb_next(n)) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + free_buffers++; + total_free_size += buffer_size; + if (buffer_size > largest_free_size) + largest_free_size = buffer_size; + } + pr_err("%d: binder_alloc_buf size %zd failed, no address space\n", + alloc->pid, size); + pr_err("allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n", + total_alloc_size, allocated_buffers, largest_alloc_size, + total_free_size, free_buffers, largest_free_size); + return ERR_PTR(-ENOSPC); + } + if (n == NULL) { + buffer = rb_entry(best_fit, struct binder_buffer, rb_node); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + } + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n", + alloc->pid, size, buffer, buffer_size); + + has_page_addr = + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK); + if (n == NULL) { + if (size + sizeof(struct binder_buffer) + 4 >= buffer_size) + buffer_size = size; /* no room for other buffers */ + else + buffer_size = size + sizeof(struct binder_buffer); + } + end_page_addr = + (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size); + if (end_page_addr > has_page_addr) + end_page_addr = has_page_addr; + ret = binder_update_page_range(alloc, 1, + (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL); + if (ret) + return ERR_PTR(ret); + + rb_erase(best_fit, &alloc->free_buffers); + buffer->free = 0; + buffer->free_in_progress = 0; + binder_insert_allocated_buffer_locked(alloc, buffer); + if (buffer_size != size) { + struct binder_buffer *new_buffer = (void *)buffer->data + size; + + list_add(&new_buffer->entry, &buffer->entry); + new_buffer->free = 1; + binder_insert_free_buffer(alloc, new_buffer); + } + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: binder_alloc_buf size %zd got %pK\n", + alloc->pid, size, buffer); + buffer->data_size = data_size; + buffer->offsets_size = offsets_size; + buffer->async_transaction = is_async; + buffer->extra_buffers_size = extra_buffers_size; + if (is_async) { + alloc->free_async_space -= size + sizeof(struct binder_buffer); + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, + "%d: binder_alloc_buf size %zd async free %zd\n", + alloc->pid, size, alloc->free_async_space); + } + return buffer; +} + +/** + * binder_alloc_new_buf() - Allocate a new binder buffer + * @alloc: binder_alloc for this proc + * @data_size: size of user data buffer + * @offsets_size: user specified buffer offset + * @extra_buffers_size: size of extra space for meta-data (eg, security context) + * @is_async: buffer for async transaction + * + * Allocate a new buffer given the requested sizes. Returns + * the kernel version of the buffer pointer. The size allocated + * is the sum of the three given sizes (each rounded up to + * pointer-sized boundary) + * + * Return: The allocated buffer or %NULL if error + */ +struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, + size_t data_size, + size_t offsets_size, + size_t extra_buffers_size, + int is_async) +{ + struct binder_buffer *buffer; + + mutex_lock(&alloc->mutex); + buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size, + extra_buffers_size, is_async); + mutex_unlock(&alloc->mutex); + return buffer; +} + +static void *buffer_start_page(struct binder_buffer *buffer) +{ + return (void *)((uintptr_t)buffer & PAGE_MASK); +} + +static void *buffer_end_page(struct binder_buffer *buffer) +{ + return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK); +} + +static void binder_delete_free_buffer(struct binder_alloc *alloc, + struct binder_buffer *buffer) +{ + struct binder_buffer *prev, *next = NULL; + int free_page_end = 1; + int free_page_start = 1; + + BUG_ON(alloc->buffers.next == &buffer->entry); + prev = list_entry(buffer->entry.prev, struct binder_buffer, entry); + BUG_ON(!prev->free); + if (buffer_end_page(prev) == buffer_start_page(buffer)) { + free_page_start = 0; + if (buffer_end_page(prev) == buffer_end_page(buffer)) + free_page_end = 0; + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: merge free, buffer %pK share page with %pK\n", + alloc->pid, buffer, prev); + } + + if (!list_is_last(&buffer->entry, &alloc->buffers)) { + next = list_entry(buffer->entry.next, + struct binder_buffer, entry); + if (buffer_start_page(next) == buffer_end_page(buffer)) { + free_page_end = 0; + if (buffer_start_page(next) == + buffer_start_page(buffer)) + free_page_start = 0; + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: merge free, buffer %pK share page with %pK\n", + alloc->pid, buffer, prev); + } + } + list_del(&buffer->entry); + if (free_page_start || free_page_end) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: merge free, buffer %pK do not share page%s%s with %pK or %pK\n", + alloc->pid, buffer, free_page_start ? "" : " end", + free_page_end ? "" : " start", prev, next); + binder_update_page_range(alloc, 0, free_page_start ? + buffer_start_page(buffer) : buffer_end_page(buffer), + (free_page_end ? buffer_end_page(buffer) : + buffer_start_page(buffer)) + PAGE_SIZE, NULL); + } +} + +static void binder_free_buf_locked(struct binder_alloc *alloc, + struct binder_buffer *buffer) +{ + size_t size, buffer_size; + + buffer_size = binder_alloc_buffer_size(alloc, buffer); + + size = ALIGN(buffer->data_size, sizeof(void *)) + + ALIGN(buffer->offsets_size, sizeof(void *)) + + ALIGN(buffer->extra_buffers_size, sizeof(void *)); + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: binder_free_buf %pK size %zd buffer_size %zd\n", + alloc->pid, buffer, size, buffer_size); + + BUG_ON(buffer->free); + BUG_ON(size > buffer_size); + BUG_ON(buffer->transaction != NULL); + BUG_ON((void *)buffer < alloc->buffer); + BUG_ON((void *)buffer > alloc->buffer + alloc->buffer_size); + + if (buffer->async_transaction) { + alloc->free_async_space += size + sizeof(struct binder_buffer); + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, + "%d: binder_free_buf size %zd async free %zd\n", + alloc->pid, size, alloc->free_async_space); + } + + binder_update_page_range(alloc, 0, + (void *)PAGE_ALIGN((uintptr_t)buffer->data), + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK), + NULL); + + rb_erase(&buffer->rb_node, &alloc->allocated_buffers); + buffer->free = 1; + if (!list_is_last(&buffer->entry, &alloc->buffers)) { + struct binder_buffer *next = list_entry(buffer->entry.next, + struct binder_buffer, entry); + + if (next->free) { + rb_erase(&next->rb_node, &alloc->free_buffers); + binder_delete_free_buffer(alloc, next); + } + } + if (alloc->buffers.next != &buffer->entry) { + struct binder_buffer *prev = list_entry(buffer->entry.prev, + struct binder_buffer, entry); + + if (prev->free) { + binder_delete_free_buffer(alloc, buffer); + rb_erase(&prev->rb_node, &alloc->free_buffers); + buffer = prev; + } + } + binder_insert_free_buffer(alloc, buffer); +} + +/** + * binder_alloc_free_buf() - free a binder buffer + * @alloc: binder_alloc for this proc + * @buffer: kernel pointer to buffer + * + * Free the buffer allocated via binder_alloc_new_buffer() + */ +void binder_alloc_free_buf(struct binder_alloc *alloc, + struct binder_buffer *buffer) +{ + mutex_lock(&alloc->mutex); + binder_free_buf_locked(alloc, buffer); + mutex_unlock(&alloc->mutex); +} + +/** + * binder_alloc_mmap_handler() - map virtual address space for proc + * @alloc: alloc structure for this proc + * @vma: vma passed to mmap() + * + * Called by binder_mmap() to initialize the space specified in + * vma for allocating binder buffers + * + * Return: + * 0 = success + * -EBUSY = address space already mapped + * -ENOMEM = failed to map memory to given address space + */ +int binder_alloc_mmap_handler(struct binder_alloc *alloc, + struct vm_area_struct *vma) +{ + int ret; + struct vm_struct *area; + const char *failure_string; + struct binder_buffer *buffer; + + mutex_lock(&binder_alloc_mmap_lock); + if (alloc->buffer) { + ret = -EBUSY; + failure_string = "already mapped"; + goto err_already_mapped; + } + + area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); + if (area == NULL) { + ret = -ENOMEM; + failure_string = "get_vm_area"; + goto err_get_vm_area_failed; + } + alloc->buffer = area->addr; + alloc->user_buffer_offset = + vma->vm_start - (uintptr_t)alloc->buffer; + mutex_unlock(&binder_alloc_mmap_lock); + +#ifdef CONFIG_CPU_CACHE_VIPT + if (cache_is_vipt_aliasing()) { + while (CACHE_COLOUR( + (vma->vm_start ^ (uint32_t)alloc->buffer))) { + pr_info("binder_mmap: %d %lx-%lx maps %pK bad alignment\n", + alloc->pid, vma->vm_start, vma->vm_end, + alloc->buffer); + vma->vm_start += PAGE_SIZE; + } + } +#endif + alloc->pages = kzalloc(sizeof(alloc->pages[0]) * + ((vma->vm_end - vma->vm_start) / PAGE_SIZE), + GFP_KERNEL); + if (alloc->pages == NULL) { + ret = -ENOMEM; + failure_string = "alloc page array"; + goto err_alloc_pages_failed; + } + alloc->buffer_size = vma->vm_end - vma->vm_start; + + if (binder_update_page_range(alloc, 1, alloc->buffer, + alloc->buffer + PAGE_SIZE, vma)) { + ret = -ENOMEM; + failure_string = "alloc small buf"; + goto err_alloc_small_buf_failed; + } + buffer = alloc->buffer; + INIT_LIST_HEAD(&alloc->buffers); + list_add(&buffer->entry, &alloc->buffers); + buffer->free = 1; + binder_insert_free_buffer(alloc, buffer); + alloc->free_async_space = alloc->buffer_size / 2; + barrier(); + alloc->vma = vma; + alloc->vma_vm_mm = vma->vm_mm; + + return 0; + +err_alloc_small_buf_failed: + kfree(alloc->pages); + alloc->pages = NULL; +err_alloc_pages_failed: + mutex_lock(&binder_alloc_mmap_lock); + vfree(alloc->buffer); + alloc->buffer = NULL; +err_get_vm_area_failed: +err_already_mapped: + mutex_unlock(&binder_alloc_mmap_lock); + pr_err("%s: %d %lx-%lx %s failed %d\n", __func__, + alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret); + return ret; +} + + +void binder_alloc_deferred_release(struct binder_alloc *alloc) +{ + struct rb_node *n; + int buffers, page_count; + + BUG_ON(alloc->vma); + + buffers = 0; + mutex_lock(&alloc->mutex); + while ((n = rb_first(&alloc->allocated_buffers))) { + struct binder_buffer *buffer; + + buffer = rb_entry(n, struct binder_buffer, rb_node); + + /* Transaction should already have been freed */ + BUG_ON(buffer->transaction); + + binder_free_buf_locked(alloc, buffer); + buffers++; + } + + page_count = 0; + if (alloc->pages) { + int i; + + for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { + void *page_addr; + + if (!alloc->pages[i]) + continue; + + page_addr = alloc->buffer + i * PAGE_SIZE; + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%s: %d: page %d at %pK not freed\n", + __func__, alloc->pid, i, page_addr); + unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); + __free_page(alloc->pages[i]); + page_count++; + } + kfree(alloc->pages); + vfree(alloc->buffer); + } + mutex_unlock(&alloc->mutex); + + binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE, + "%s: %d buffers %d, pages %d\n", + __func__, alloc->pid, buffers, page_count); +} + +static void print_binder_buffer(struct seq_file *m, const char *prefix, + struct binder_buffer *buffer) +{ + seq_printf(m, "%s %d: %pK size %zd:%zd:%zd %s\n", + prefix, buffer->debug_id, buffer->data, + buffer->data_size, buffer->offsets_size, + buffer->extra_buffers_size, + buffer->transaction ? "active" : "delivered"); +} + +/** + * binder_alloc_print_allocated() - print buffer info + * @m: seq_file for output via seq_printf() + * @alloc: binder_alloc for this proc + * + * Prints information about every buffer associated with + * the binder_alloc state to the given seq_file + */ +void binder_alloc_print_allocated(struct seq_file *m, + struct binder_alloc *alloc) +{ + struct rb_node *n; + + mutex_lock(&alloc->mutex); + for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) + print_binder_buffer(m, " buffer", + rb_entry(n, struct binder_buffer, rb_node)); + mutex_unlock(&alloc->mutex); +} + +/** + * binder_alloc_get_allocated_count() - return count of buffers + * @alloc: binder_alloc for this proc + * + * Return: count of allocated buffers + */ +int binder_alloc_get_allocated_count(struct binder_alloc *alloc) +{ + struct rb_node *n; + int count = 0; + + mutex_lock(&alloc->mutex); + for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) + count++; + mutex_unlock(&alloc->mutex); + return count; +} + + +/** + * binder_alloc_vma_close() - invalidate address space + * @alloc: binder_alloc for this proc + * + * Called from binder_vma_close() when releasing address space. + * Clears alloc->vma to prevent new incoming transactions from + * allocating more buffers. + */ +void binder_alloc_vma_close(struct binder_alloc *alloc) +{ + WRITE_ONCE(alloc->vma, NULL); + WRITE_ONCE(alloc->vma_vm_mm, NULL); +} + +/** + * binder_alloc_init() - called by binder_open() for per-proc initialization + * @alloc: binder_alloc for this proc + * + * Called from binder_open() to initialize binder_alloc fields for + * new binder proc + */ +void binder_alloc_init(struct binder_alloc *alloc) +{ + alloc->tsk = current->group_leader; + alloc->pid = current->group_leader->pid; + mutex_init(&alloc->mutex); +} + diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h new file mode 100644 index 000000000000..088e4ffc6230 --- /dev/null +++ b/drivers/android/binder_alloc.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2017 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_BINDER_ALLOC_H +#define _LINUX_BINDER_ALLOC_H + +#include <linux/rbtree.h> +#include <linux/list.h> +#include <linux/mm.h> +#include <linux/rtmutex.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> + +struct binder_transaction; + +/** + * struct binder_buffer - buffer used for binder transactions + * @entry: entry alloc->buffers + * @rb_node: node for allocated_buffers/free_buffers rb trees + * @free: true if buffer is free + * @allow_user_free: describe the second member of struct blah, + * @async_transaction: describe the second member of struct blah, + * @debug_id: describe the second member of struct blah, + * @transaction: describe the second member of struct blah, + * @target_node: describe the second member of struct blah, + * @data_size: describe the second member of struct blah, + * @offsets_size: describe the second member of struct blah, + * @extra_buffers_size: describe the second member of struct blah, + * @data:i describe the second member of struct blah, + * + * Bookkeeping structure for binder transaction buffers + */ +struct binder_buffer { + struct list_head entry; /* free and allocated entries by address */ + struct rb_node rb_node; /* free entry by size or allocated entry */ + /* by address */ + unsigned free:1; + unsigned allow_user_free:1; + unsigned async_transaction:1; + unsigned free_in_progress:1; + unsigned debug_id:28; + + struct binder_transaction *transaction; + + struct binder_node *target_node; + size_t data_size; + size_t offsets_size; + size_t extra_buffers_size; + uint8_t data[0]; +}; + +/** + * struct binder_alloc - per-binder proc state for binder allocator + * @vma: vm_area_struct passed to mmap_handler + * (invarient after mmap) + * @tsk: tid for task that called init for this proc + * (invariant after init) + * @vma_vm_mm: copy of vma->vm_mm (invarient after mmap) + * @buffer: base of per-proc address space mapped via mmap + * @user_buffer_offset: offset between user and kernel VAs for buffer + * @buffers: list of all buffers for this proc + * @free_buffers: rb tree of buffers available for allocation + * sorted by size + * @allocated_buffers: rb tree of allocated buffers sorted by address + * @free_async_space: VA space available for async buffers. This is + * initialized at mmap time to 1/2 the full VA space + * @pages: array of physical page addresses for each + * page of mmap'd space + * @buffer_size: size of address space specified via mmap + * @pid: pid for associated binder_proc (invariant after init) + * + * Bookkeeping structure for per-proc address space management for binder + * buffers. It is normally initialized during binder_init() and binder_mmap() + * calls. The address space is used for both user-visible buffers and for + * struct binder_buffer objects used to track the user buffers + */ +struct binder_alloc { + struct mutex mutex; + struct task_struct *tsk; + struct vm_area_struct *vma; + struct mm_struct *vma_vm_mm; + void *buffer; + ptrdiff_t user_buffer_offset; + struct list_head buffers; + struct rb_root free_buffers; + struct rb_root allocated_buffers; + size_t free_async_space; + struct page **pages; + size_t buffer_size; + uint32_t buffer_free; + int pid; +}; + +extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, + size_t data_size, + size_t offsets_size, + size_t extra_buffers_size, + int is_async); +extern void binder_alloc_init(struct binder_alloc *alloc); +extern void binder_alloc_vma_close(struct binder_alloc *alloc); +extern struct binder_buffer * +binder_alloc_prepare_to_free(struct binder_alloc *alloc, + uintptr_t user_ptr); +extern void binder_alloc_free_buf(struct binder_alloc *alloc, + struct binder_buffer *buffer); +extern int binder_alloc_mmap_handler(struct binder_alloc *alloc, + struct vm_area_struct *vma); +extern void binder_alloc_deferred_release(struct binder_alloc *alloc); +extern int binder_alloc_get_allocated_count(struct binder_alloc *alloc); +extern void binder_alloc_print_allocated(struct seq_file *m, + struct binder_alloc *alloc); + +/** + * binder_alloc_get_free_async_space() - get free space available for async + * @alloc: binder_alloc for this proc + * + * Return: the bytes remaining in the address-space for async transactions + */ +static inline size_t +binder_alloc_get_free_async_space(struct binder_alloc *alloc) +{ + size_t free_async_space; + + mutex_lock(&alloc->mutex); + free_async_space = alloc->free_async_space; + mutex_unlock(&alloc->mutex); + return free_async_space; +} + +/** + * binder_alloc_get_user_buffer_offset() - get offset between kernel/user addrs + * @alloc: binder_alloc for this proc + * + * Return: the offset between kernel and user-space addresses to use for + * virtual address conversion + */ +static inline ptrdiff_t +binder_alloc_get_user_buffer_offset(struct binder_alloc *alloc) +{ + /* + * user_buffer_offset is constant if vma is set and + * undefined if vma is not set. It is possible to + * get here with !alloc->vma if the target process + * is dying while a transaction is being initiated. + * Returning the old value is ok in this case and + * the transaction will fail. + */ + return alloc->user_buffer_offset; +} + +#endif /* _LINUX_BINDER_ALLOC_H */ + diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index 7f20f3dc8369..7967db16ba5a 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -23,7 +23,8 @@ struct binder_buffer; struct binder_node; struct binder_proc; -struct binder_ref; +struct binder_alloc; +struct binder_ref_data; struct binder_thread; struct binder_transaction; @@ -146,8 +147,8 @@ TRACE_EVENT(binder_transaction_received, TRACE_EVENT(binder_transaction_node_to_ref, TP_PROTO(struct binder_transaction *t, struct binder_node *node, - struct binder_ref *ref), - TP_ARGS(t, node, ref), + struct binder_ref_data *rdata), + TP_ARGS(t, node, rdata), TP_STRUCT__entry( __field(int, debug_id) @@ -160,8 +161,8 @@ TRACE_EVENT(binder_transaction_node_to_ref, __entry->debug_id = t->debug_id; __entry->node_debug_id = node->debug_id; __entry->node_ptr = node->ptr; - __entry->ref_debug_id = ref->debug_id; - __entry->ref_desc = ref->desc; + __entry->ref_debug_id = rdata->debug_id; + __entry->ref_desc = rdata->desc; ), TP_printk("transaction=%d node=%d src_ptr=0x%016llx ==> dest_ref=%d dest_desc=%d", __entry->debug_id, __entry->node_debug_id, @@ -170,8 +171,9 @@ TRACE_EVENT(binder_transaction_node_to_ref, ); TRACE_EVENT(binder_transaction_ref_to_node, - TP_PROTO(struct binder_transaction *t, struct binder_ref *ref), - TP_ARGS(t, ref), + TP_PROTO(struct binder_transaction *t, struct binder_node *node, + struct binder_ref_data *rdata), + TP_ARGS(t, node, rdata), TP_STRUCT__entry( __field(int, debug_id) @@ -182,10 +184,10 @@ TRACE_EVENT(binder_transaction_ref_to_node, ), TP_fast_assign( __entry->debug_id = t->debug_id; - __entry->ref_debug_id = ref->debug_id; - __entry->ref_desc = ref->desc; - __entry->node_debug_id = ref->node->debug_id; - __entry->node_ptr = ref->node->ptr; + __entry->ref_debug_id = rdata->debug_id; + __entry->ref_desc = rdata->desc; + __entry->node_debug_id = node->debug_id; + __entry->node_ptr = node->ptr; ), TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%016llx", __entry->debug_id, __entry->node_debug_id, @@ -194,9 +196,10 @@ TRACE_EVENT(binder_transaction_ref_to_node, ); TRACE_EVENT(binder_transaction_ref_to_ref, - TP_PROTO(struct binder_transaction *t, struct binder_ref *src_ref, - struct binder_ref *dest_ref), - TP_ARGS(t, src_ref, dest_ref), + TP_PROTO(struct binder_transaction *t, struct binder_node *node, + struct binder_ref_data *src_ref, + struct binder_ref_data *dest_ref), + TP_ARGS(t, node, src_ref, dest_ref), TP_STRUCT__entry( __field(int, debug_id) @@ -208,7 +211,7 @@ TRACE_EVENT(binder_transaction_ref_to_ref, ), TP_fast_assign( __entry->debug_id = t->debug_id; - __entry->node_debug_id = src_ref->node->debug_id; + __entry->node_debug_id = node->debug_id; __entry->src_ref_debug_id = src_ref->debug_id; __entry->src_ref_desc = src_ref->desc; __entry->dest_ref_debug_id = dest_ref->debug_id; @@ -268,9 +271,9 @@ DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release, TP_ARGS(buffer)); TRACE_EVENT(binder_update_page_range, - TP_PROTO(struct binder_proc *proc, bool allocate, + TP_PROTO(struct binder_alloc *alloc, bool allocate, void *start, void *end), - TP_ARGS(proc, allocate, start, end), + TP_ARGS(alloc, allocate, start, end), TP_STRUCT__entry( __field(int, proc) __field(bool, allocate) @@ -278,9 +281,9 @@ TRACE_EVENT(binder_update_page_range, __field(size_t, size) ), TP_fast_assign( - __entry->proc = proc->pid; + __entry->proc = alloc->pid; __entry->allocate = allocate; - __entry->offset = start - proc->buffer; + __entry->offset = start - alloc->buffer; __entry->size = end - start; ), TP_printk("proc=%d allocate=%d offset=%zu size=%zu", diff --git a/drivers/base/core.c b/drivers/base/core.c index 3fa9096b27c2..f3d395bfe8f6 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1116,13 +1116,7 @@ int device_add(struct device *dev) error = dpm_sysfs_add(dev); if (error) goto DPMError; - if ((dev->pm_domain) || (dev->type && dev->type->pm) - || (dev->class && (dev->class->pm || dev->class->resume)) - || (dev->bus && (dev->bus->pm || dev->bus->resume)) || - (dev->driver && dev->driver->pm)) { - device_pm_add(dev); - } - + device_pm_add(dev); if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); @@ -2105,7 +2099,11 @@ void device_shutdown(void) pm_runtime_get_noresume(dev); pm_runtime_barrier(dev); - if (dev->bus && dev->bus->shutdown) { + if (dev->class && dev->class->shutdown) { + if (initcall_debug) + dev_info(dev, "shutdown\n"); + dev->class->shutdown(dev); + } else if (dev->bus && dev->bus->shutdown) { if (initcall_debug) dev_info(dev, "shutdown\n"); dev->bus->shutdown(dev); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 9920916a6220..ae7f3ce90bd2 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -827,7 +827,7 @@ static ssize_t driver_override_store(struct device *dev, const char *buf, size_t count) { struct platform_device *pdev = to_platform_device(dev); - char *driver_override, *old = pdev->driver_override, *cp; + char *driver_override, *old, *cp; if (count > PATH_MAX) return -EINVAL; @@ -840,12 +840,15 @@ static ssize_t driver_override_store(struct device *dev, if (cp) *cp = '\0'; + device_lock(dev); + old = pdev->driver_override; if (strlen(driver_override)) { pdev->driver_override = driver_override; } else { kfree(driver_override); pdev->driver_override = NULL; } + device_unlock(dev); kfree(old); @@ -856,8 +859,12 @@ static ssize_t driver_override_show(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); + ssize_t len; - return sprintf(buf, "%s\n", pdev->driver_override); + device_lock(dev); + len = sprintf(buf, "%s\n", pdev->driver_override); + device_unlock(dev); + return len; } static DEVICE_ATTR_RW(driver_override); diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index a48824deabc5..78b0ece0c867 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1188,7 +1188,6 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, } dev->power.subsys_data->domain_data = &gpd_data->base; - dev->pm_domain = &genpd->domain; spin_unlock_irq(&dev->power.lock); @@ -1207,7 +1206,6 @@ static void genpd_free_dev_data(struct device *dev, { spin_lock_irq(&dev->power.lock); - dev->pm_domain = NULL; dev->power.subsys_data->domain_data = NULL; spin_unlock_irq(&dev->power.lock); @@ -1248,6 +1246,8 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, if (ret) goto out; + dev->pm_domain = &genpd->domain; + genpd->device_count++; genpd->max_off_time_changed = true; @@ -1299,6 +1299,8 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, if (genpd->detach_dev) genpd->detach_dev(genpd, dev); + dev->pm_domain = NULL; + list_del_init(&pdd->list_node); mutex_unlock(&genpd->lock); @@ -1373,7 +1375,7 @@ EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *subdomain) { - struct gpd_link *link; + struct gpd_link *l, *link; int ret = -EINVAL; if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) @@ -1388,7 +1390,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, goto out; } - list_for_each_entry(link, &genpd->master_links, master_node) { + list_for_each_entry_safe(link, l, &genpd->master_links, master_node) { if (link->slave != subdomain) continue; @@ -1642,10 +1644,10 @@ EXPORT_SYMBOL_GPL(__of_genpd_add_provider); */ void of_genpd_del_provider(struct device_node *np) { - struct of_genpd_provider *cp; + struct of_genpd_provider *cp, *tmp; mutex_lock(&of_genpd_mutex); - list_for_each_entry(cp, &of_genpd_providers, link) { + list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) { if (cp->node == np) { list_del(&cp->link); of_node_put(cp->node); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index a88590bb0b10..6c5bc3fadfcf 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -162,12 +162,6 @@ void device_pm_move_before(struct device *deva, struct device *devb) pr_debug("PM: Moving %s:%s before %s:%s\n", deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); - if (!((devb->pm_domain) || (devb->type && devb->type->pm) - || (devb->class && (devb->class->pm || devb->class->resume)) - || (devb->bus && (devb->bus->pm || devb->bus->resume)) || - (devb->driver && devb->driver->pm))) { - device_pm_add(devb); - } /* Delete deva from dpm_list and reinsert before devb. */ list_move_tail(&deva->power.entry, &devb->power.entry); } @@ -182,12 +176,6 @@ void device_pm_move_after(struct device *deva, struct device *devb) pr_debug("PM: Moving %s:%s after %s:%s\n", deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); - if (!((devb->pm_domain) || (devb->type && devb->type->pm) - || (devb->class && (devb->class->pm || devb->class->resume)) - || (devb->bus && (devb->bus->pm || devb->bus->resume)) || - (devb->driver && devb->driver->pm))) { - device_pm_add(devb); - } /* Delete deva from dpm_list and reinsert after devb. */ list_move(&deva->power.entry, &devb->power.entry); } diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index a7b46798c81d..39efa7e6c0c0 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -268,6 +268,8 @@ static ssize_t pm_qos_latency_tolerance_store(struct device *dev, value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; else if (!strcmp(buf, "any") || !strcmp(buf, "any\n")) value = PM_QOS_LATENCY_ANY; + else + return -EINVAL; } ret = dev_pm_qos_update_user_latency_tolerance(dev, value); return ret < 0 ? ret : n; diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 0e494108c20c..7af116e12e53 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -61,6 +61,8 @@ static LIST_HEAD(wakeup_sources); static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue); +DEFINE_STATIC_SRCU(wakeup_srcu); + static struct wakeup_source deleted_ws = { .name = "deleted", .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock), @@ -199,7 +201,7 @@ void wakeup_source_remove(struct wakeup_source *ws) spin_lock_irqsave(&events_lock, flags); list_del_rcu(&ws->entry); spin_unlock_irqrestore(&events_lock, flags); - synchronize_rcu(); + synchronize_srcu(&wakeup_srcu); } EXPORT_SYMBOL_GPL(wakeup_source_remove); @@ -331,13 +333,14 @@ void device_wakeup_detach_irq(struct device *dev) void device_wakeup_arm_wake_irqs(void) { struct wakeup_source *ws; + int srcuidx; - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { if (ws->wakeirq) dev_pm_arm_wake_irq(ws->wakeirq); } - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); } /** @@ -348,13 +351,14 @@ void device_wakeup_arm_wake_irqs(void) void device_wakeup_disarm_wake_irqs(void) { struct wakeup_source *ws; + int srcuidx; - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { if (ws->wakeirq) dev_pm_disarm_wake_irq(ws->wakeirq); } - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); } /** @@ -839,10 +843,10 @@ EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources); void pm_print_active_wakeup_sources(void) { struct wakeup_source *ws; - int active = 0; + int srcuidx, active = 0; struct wakeup_source *last_activity_ws = NULL; - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { if (ws->active) { pr_info("active wakeup source: %s\n", ws->name); @@ -858,7 +862,7 @@ void pm_print_active_wakeup_sources(void) if (!active && last_activity_ws) pr_info("last active wakeup source: %s\n", last_activity_ws->name); - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); } EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources); @@ -985,8 +989,9 @@ void pm_wakep_autosleep_enabled(bool set) { struct wakeup_source *ws; ktime_t now = ktime_get(); + int srcuidx; - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { spin_lock_irq(&ws->lock); if (ws->autosleep_enabled != set) { @@ -1000,7 +1005,7 @@ void pm_wakep_autosleep_enabled(bool set) } spin_unlock_irq(&ws->lock); } - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); } #endif /* CONFIG_PM_AUTOSLEEP */ @@ -1061,15 +1066,16 @@ static int print_wakeup_source_stats(struct seq_file *m, static int wakeup_sources_stats_show(struct seq_file *m, void *unused) { struct wakeup_source *ws; + int srcuidx; seq_puts(m, "name\t\t\t\t\tactive_count\tevent_count\twakeup_count\t" "expire_count\tactive_since\ttotal_time\tmax_time\t" "last_change\tprevent_suspend_time\n"); - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) print_wakeup_source_stats(m, ws); - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); print_wakeup_source_stats(m, &deleted_ws); diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 41fb1a917b17..33e23a7a691f 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -595,8 +595,6 @@ int xen_blkif_schedule(void *arg) unsigned long timeout; int ret; - xen_blkif_get(blkif); - while (!kthread_should_stop()) { if (try_to_freeze()) continue; @@ -650,7 +648,6 @@ purge_gnt_list: print_stats(blkif); blkif->xenblkd = NULL; - xen_blkif_put(blkif); return 0; } diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index f53cff42f8da..923308201375 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -221,7 +221,6 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif) if (blkif->xenblkd) { kthread_stop(blkif->xenblkd); wake_up(&blkif->shutdown_wq); - blkif->xenblkd = NULL; } /* The above kthread_stop() guarantees that at this point we @@ -266,9 +265,10 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif) static void xen_blkif_free(struct xen_blkif *blkif) { - - xen_blkif_disconnect(blkif); + WARN_ON(xen_blkif_disconnect(blkif)); xen_vbd_free(&blkif->vbd); + kfree(blkif->be->mode); + kfree(blkif->be); /* Make sure everything is drained before shutting down */ BUG_ON(blkif->persistent_gnt_c != 0); @@ -445,8 +445,6 @@ static int xen_blkbk_remove(struct xenbus_device *dev) xen_blkif_put(be->blkif); } - kfree(be->mode); - kfree(be); return 0; } diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c index 969f755f5dc4..0a61186167ba 100644 --- a/drivers/bluetooth/btfm_slim.c +++ b/drivers/bluetooth/btfm_slim.c @@ -155,23 +155,22 @@ int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, rxport, 1); if (ret < 0) { BTFMSLIM_ERR("vendor_port_en failed ret[%d]", - ret); + ret); goto error; } } if (rxport) { BTFMSLIM_INFO("slim_connect_sink(port: %d, ch: %d)", - ch->port, ch->ch); + ch->port, ch->ch); /* Connect Port with channel given by Machine driver*/ ret = slim_connect_sink(btfmslim->slim_pgd, &ch->port_hdl, 1, ch->ch_hdl); if (ret < 0) { BTFMSLIM_ERR("slim_connect_sink failed ret[%d]", - ret); + ret); goto remove_channel; } - } else { BTFMSLIM_INFO("slim_connect_src(port: %d, ch: %d)", ch->port, ch->ch); @@ -180,7 +179,7 @@ int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, ch->ch_hdl); if (ret < 0) { BTFMSLIM_ERR("slim_connect_src failed ret[%d]", - ret); + ret); goto remove_channel; } } @@ -190,6 +189,7 @@ int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, BTFMSLIM_INFO( "port: %d, ch: %d, grp: %d, ch->grph: 0x%x, ch_hdl: 0x%x", chan->port, chan->ch, grp, chan->grph, chan->ch_hdl); + ret = slim_control_ch(btfmslim->slim_pgd, (grp ? chan->grph : chan->ch_hdl), SLIM_CH_ACTIVATE, true); if (ret < 0) { @@ -220,6 +220,7 @@ int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, BTFMSLIM_INFO("port:%d, grp: %d, ch->grph:0x%x, ch->ch_hdl:0x%x ", ch->port, grp, ch->grph, ch->ch_hdl); + /* Remove the channel immediately*/ ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl), SLIM_CH_REMOVE, true); @@ -233,7 +234,6 @@ int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, goto error; } } - /* Disable port through registration setting */ for (i = 0; i < nchan; i++, ch++) { if (btfmslim->vendor_port_en) { @@ -246,9 +246,11 @@ int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, } } } + error: return ret; } + static int btfm_slim_get_logical_addr(struct slim_device *slim) { int ret = 0; diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c index 96be0e2f9183..035e8d9fb5fd 100644 --- a/drivers/bluetooth/btfm_slim_codec.c +++ b/drivers/bluetooth/btfm_slim_codec.c @@ -118,9 +118,6 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream, return; } - if (dai->id == BTFM_FM_SLIM_TX) - goto out; - /* Search for dai->id matched port handler */ for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) && @@ -134,7 +131,6 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream, } btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan); -out: btfm_slim_hw_deinit(btfmslim); } @@ -205,61 +201,6 @@ int btfm_slim_dai_prepare(struct snd_pcm_substream *substream, return ret; } -static int btfm_slim_dai_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - int ret = -EINVAL, i; - struct btfmslim *btfmslim = dai->dev->platform_data; - struct btfmslim_ch *ch; - uint8_t rxport, grp = false, nchan = 1; - - BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name, - dai->id, dai->rate); - - switch (dai->id) { - case BTFM_FM_SLIM_TX: - grp = true; nchan = 2; - ch = btfmslim->tx_chs; - rxport = 0; - break; - case BTFM_BT_SCO_SLIM_TX: - ch = btfmslim->tx_chs; - rxport = 0; - break; - case BTFM_BT_SCO_A2DP_SLIM_RX: - case BTFM_BT_SPLIT_A2DP_SLIM_RX: - ch = btfmslim->rx_chs; - rxport = 1; - break; - case BTFM_SLIM_NUM_CODEC_DAIS: - default: - BTFMSLIM_ERR("dai->id is invalid:%d", dai->id); - goto out; - } - - if (dai->id != BTFM_FM_SLIM_TX) { - ret = 0; - goto out; - } - - /* Search for dai->id matched port handler */ - for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && - (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) && - (ch->id != dai->id); ch++, i++) - ; - - if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) || - (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) { - BTFMSLIM_ERR("ch is invalid!!"); - goto out; - } - - btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan); - -out: - return ret; -} - /* This function will be called once during boot up */ static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, @@ -402,7 +343,6 @@ static struct snd_soc_dai_ops btfmslim_dai_ops = { .shutdown = btfm_slim_dai_shutdown, .hw_params = btfm_slim_dai_hw_params, .prepare = btfm_slim_dai_prepare, - .hw_free = btfm_slim_dai_hw_free, .set_channel_map = btfm_slim_dai_set_channel_map, .get_channel_map = btfm_slim_dai_get_channel_map, }; diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c index 0c4e0b3d5c2e..7abd5598c47b 100644 --- a/drivers/bluetooth/btfm_slim_wcn3990.c +++ b/drivers/bluetooth/btfm_slim_wcn3990.c @@ -82,11 +82,12 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, uint8_t rxport, uint8_t enable) { int ret = 0; - uint8_t reg_val = 0; + uint8_t reg_val = 0, en; uint8_t port_bit = 0; uint16_t reg; BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable); + if (rxport) { if (enable) { /* For SCO Rx, A2DP Rx */ @@ -117,7 +118,20 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); if (ret) { - BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", + ret, reg); + goto error; + } + } else if (port_num == CHRK_SB_PGD_PORT_TX_SCO) { + /* SCO Tx */ + reg_val = 0x1 << CHRK_SB_PGD_PORT_TX_SCO; + reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); + BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", + reg_val, reg); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", + ret, reg); goto error; } } @@ -137,15 +151,21 @@ enable_disable_txport: reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num); enable_disable_rxport: - if (enable) { - if (is_fm_port(port_num)) - reg_val = CHRK_SB_PGD_PORT_ENABLE | - CHRK_SB_PGD_PORT_WM_L3; - else - reg_val = CHRK_SB_PGD_PORT_ENABLE | - CHRK_SB_PGD_PORT_WM_LB; - } else - reg_val = CHRK_SB_PGD_PORT_DISABLE; + if (enable) + en = CHRK_SB_PGD_PORT_ENABLE; + else + en = CHRK_SB_PGD_PORT_DISABLE; + + if (is_fm_port(port_num)) + reg_val = en | CHRK_SB_PGD_PORT_WM_L8; + else if (port_num == CHRK_SB_PGD_PORT_TX_SCO) + reg_val = enable ? en | CHRK_SB_PGD_PORT_WM_L1 : en; + else + reg_val = enable ? en | CHRK_SB_PGD_PORT_WM_LB : en; + + if (enable && port_num == CHRK_SB_PGD_PORT_TX_SCO) + BTFMSLIM_INFO("programming SCO Tx with reg_val %d to reg 0x%x", + reg_val, reg); ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); if (ret) diff --git a/drivers/bluetooth/btfm_slim_wcn3990.h b/drivers/bluetooth/btfm_slim_wcn3990.h index f6a260096c91..b637ac581201 100644 --- a/drivers/bluetooth/btfm_slim_wcn3990.h +++ b/drivers/bluetooth/btfm_slim_wcn3990.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -68,6 +68,7 @@ #define CHRK_SB_PGD_PORT_WM_L1 (0x1 << 1) #define CHRK_SB_PGD_PORT_WM_L2 (0x2 << 1) #define CHRK_SB_PGD_PORT_WM_L3 (0x3 << 1) +#define CHRK_SB_PGD_PORT_WM_L8 (0x8 << 1) #define CHRK_SB_PGD_PORT_WM_LB (0xB << 1) #define CHRK_SB_PGD_PORT_RX_NUM 16 diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index a84172106e0f..77c8f279b4f5 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -58,6 +58,7 @@ #define FASTRPC_ENOSUCH 39 #define VMID_SSC_Q6 5 #define VMID_ADSP_Q6 6 +#define AC_VM_ADSP_HEAP_SHARED 33 #define DEBUGFS_SIZE 1024 #define RPC_TIMEOUT (5 * HZ) @@ -222,6 +223,7 @@ struct fastrpc_channel_ctx { int prevssrcount; int issubsystemup; int vmid; + int heap_vmid; int ramdumpenabled; void *remoteheap_ramdump_dev; struct fastrpc_glink_info link; @@ -291,6 +293,7 @@ struct fastrpc_file { int cid; int ssrcount; int pd; + int file_close; struct fastrpc_apps *apps; struct fastrpc_perf perf; struct dentry *debugfs_file; @@ -319,6 +322,7 @@ static struct fastrpc_channel_ctx gcinfo[NUM_CHANNELS] = { .channel = SMD_APPS_DSPS, .link.link_info.edge = "dsps", .link.link_info.transport = "smem", + .vmid = VMID_SSC_Q6, }, { .name = "cdsprpc-smd", @@ -699,7 +703,7 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, unsigned attr, if (vmid) { int srcVM[1] = {VMID_HLOS}; int destVM[2] = {VMID_HLOS, vmid}; - int destVMperm[2] = {PERM_READ | PERM_WRITE, + int destVMperm[2] = {PERM_READ | PERM_WRITE | PERM_EXEC, PERM_READ | PERM_WRITE | PERM_EXEC}; VERIFY(err, !hyp_assign_phys(map->phys, @@ -771,7 +775,7 @@ static int fastrpc_buf_alloc(struct fastrpc_file *fl, ssize_t size, if (vmid) { int srcVM[1] = {VMID_HLOS}; int destVM[2] = {VMID_HLOS, vmid}; - int destVMperm[2] = {PERM_READ | PERM_WRITE, + int destVMperm[2] = {PERM_READ | PERM_WRITE | PERM_EXEC, PERM_READ | PERM_WRITE | PERM_EXEC}; VERIFY(err, !hyp_assign_phys(buf->phys, buf_page_size(size), @@ -1252,9 +1256,18 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) if (map && (map->attr & FASTRPC_ATTR_COHERENT)) continue; - if (rpra[i].buf.len && ctx->overps[oix]->mstart) - dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv), - uint64_to_ptr(rpra[i].buf.pv + rpra[i].buf.len)); + if (rpra[i].buf.len && ctx->overps[oix]->mstart) { + if (map && map->handle) + msm_ion_do_cache_op(ctx->fl->apps->client, + map->handle, + uint64_to_ptr(rpra[i].buf.pv), + rpra[i].buf.len, + ION_IOC_CLEAN_INV_CACHES); + else + dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv), + uint64_to_ptr(rpra[i].buf.pv + + rpra[i].buf.len)); + } } PERF_END); @@ -1265,11 +1278,6 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) rpra[inh + i].h = ctx->lpra[inh + i].h; } - if (!ctx->fl->sctx->smmu.coherent) { - PERF(ctx->fl->profile, ctx->fl->perf.flush, - dmac_flush_range((char *)rpra, (char *)rpra + ctx->used); - PERF_END); - } bail: return err; } @@ -1333,14 +1341,33 @@ static void inv_args_pre(struct smq_invoke_ctx *ctx) if (buf_page_start(ptr_to_uint64((void *)rpra)) == buf_page_start(rpra[i].buf.pv)) continue; - if (!IS_CACHE_ALIGNED((uintptr_t)uint64_to_ptr(rpra[i].buf.pv))) - dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv), - (char *)(uint64_to_ptr(rpra[i].buf.pv + 1))); + if (!IS_CACHE_ALIGNED((uintptr_t) + uint64_to_ptr(rpra[i].buf.pv))) { + if (map && map->handle) + msm_ion_do_cache_op(ctx->fl->apps->client, + map->handle, + uint64_to_ptr(rpra[i].buf.pv), + sizeof(uintptr_t), + ION_IOC_CLEAN_INV_CACHES); + else + dmac_flush_range( + uint64_to_ptr(rpra[i].buf.pv), (char *) + uint64_to_ptr(rpra[i].buf.pv + 1)); + } + end = (uintptr_t)uint64_to_ptr(rpra[i].buf.pv + rpra[i].buf.len); - if (!IS_CACHE_ALIGNED(end)) - dmac_flush_range((char *)end, - (char *)end + 1); + if (!IS_CACHE_ALIGNED(end)) { + if (map && map->handle) + msm_ion_do_cache_op(ctx->fl->apps->client, + map->handle, + uint64_to_ptr(end), + sizeof(uintptr_t), + ION_IOC_CLEAN_INV_CACHES); + else + dmac_flush_range((char *)end, + (char *)end + 1); + } } } @@ -1349,7 +1376,6 @@ static void inv_args(struct smq_invoke_ctx *ctx) int i, inbufs, outbufs; uint32_t sc = ctx->sc; remote_arg64_t *rpra = ctx->rpra; - int used = ctx->used; int inv = 0; inbufs = REMOTE_SCALARS_INBUFS(sc); @@ -1382,8 +1408,6 @@ static void inv_args(struct smq_invoke_ctx *ctx) + rpra[i].buf.len)); } - if (inv || REMOTE_SCALARS_OUTHANDLES(sc)) - dmac_inv_range(rpra, (char *)rpra + used); } static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx, @@ -1454,7 +1478,7 @@ static void smd_event_handler(void *priv, unsigned event) switch (event) { case SMD_EVENT_OPEN: - complete(&me->channel[cid].work); + complete(&me->channel[cid].workport); break; case SMD_EVENT_CLOSE: fastrpc_notify_drivers(me, cid); @@ -1475,7 +1499,7 @@ static void fastrpc_init(struct fastrpc_apps *me) me->channel = &gcinfo[0]; for (i = 0; i < NUM_CHANNELS; i++) { init_completion(&me->channel[i].work); - init_completion(&me->channel[i].workport); + init_completion(&me->channel[i].workport); me->channel[i].sesscount = 0; } } @@ -1592,7 +1616,7 @@ static int fastrpc_init_process(struct fastrpc_file *fl, struct fastrpc_mmap *file = 0, *mem = 0; char *proc_name = NULL; int srcVM[1] = {VMID_HLOS}; - int destVM[1] = {VMID_ADSP_Q6}; + int destVM[1] = {gcinfo[0].heap_vmid}; int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC}; int hlosVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC}; @@ -1851,7 +1875,7 @@ static int fastrpc_mmap_on_dsp(struct fastrpc_file *fl, uint32_t flags, } else if (flags == ADSP_MMAP_REMOTE_HEAP_ADDR) { int srcVM[1] = {VMID_HLOS}; - int destVM[1] = {VMID_ADSP_Q6}; + int destVM[1] = {gcinfo[0].heap_vmid}; int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC}; VERIFY(err, !hyp_assign_phys(map->phys, (uint64_t)map->size, @@ -1867,7 +1891,7 @@ static int fastrpc_munmap_on_dsp_rh(struct fastrpc_file *fl, struct fastrpc_mmap *map) { int err = 0; - int srcVM[1] = {VMID_ADSP_Q6}; + int srcVM[1] = {gcinfo[0].heap_vmid}; int destVM[1] = {VMID_HLOS}; int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC}; @@ -2195,6 +2219,9 @@ static int fastrpc_file_free(struct fastrpc_file *fl) return 0; } (void)fastrpc_release_current_dsp_process(fl); + spin_lock(&fl->hlock); + fl->file_close = 1; + spin_unlock(&fl->hlock); fastrpc_context_list_dtor(fl); fastrpc_buf_list_free(fl); hlist_for_each_entry_safe(map, n, &fl->maps, hn) { @@ -2395,16 +2422,16 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, spin_lock(&fl->hlock); hlist_for_each_entry_safe(buf, n, &fl->bufs, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, - "%s %p %s %p %s %llx\n", "buf:", - buf, "buf->virt:", buf->virt, - "buf->phys:", buf->phys); + "%s %pK %s %pK %s %llx\n", "buf:", + buf, "buf->virt:", buf->virt, + "buf->phys:", buf->phys); } len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "\n%s\n", "LIST OF MAPS:"); hlist_for_each_entry_safe(map, n, &fl->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, - "%s %p %s %lx %s %llx\n", + "%s %pK %s %lx %s %llx\n", "map:", map, "map->va:", map->va, "map->phys:", map->phys); @@ -2414,7 +2441,7 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, "LIST OF PENDING SMQCONTEXTS:"); hlist_for_each_entry_safe(ictx, n, &fl->clst.pending, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, - "%s %p %s %u %s %u %s %u\n", + "%s %pK %s %u %s %u %s %u\n", "smqcontext:", ictx, "sc:", ictx->sc, "tid:", ictx->pid, @@ -2425,7 +2452,7 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, "LIST OF INTERRUPTED SMQCONTEXTS:"); hlist_for_each_entry_safe(ictx, n, &fl->clst.interrupted, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, - "%s %p %s %u %s %u %s %u\n", + "%s %pK %s %u %s %u %s %u\n", "smqcontext:", ictx, "sc:", ictx->sc, "tid:", ictx->pid, @@ -2495,6 +2522,16 @@ static int fastrpc_channel_open(struct fastrpc_file *fl) kref_init(&me->channel[cid].kref); pr_info("'opened /dev/%s c %d %d'\n", gcinfo[cid].name, MAJOR(me->dev_no), cid); + + if (me->glink) { + err = glink_queue_rx_intent(me->channel[cid].chan, + NULL, 16); + err |= glink_queue_rx_intent(me->channel[cid].chan, + NULL, 64); + if (err) + pr_warn("adsprpc: intent fail for %d err %d\n", + cid, err); + } if (cid == 0 && me->channel[cid].ssrcount != me->channel[cid].prevssrcount) { if (fastrpc_mmap_remove_ssr(fl)) @@ -2583,6 +2620,14 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num, p.inv.fds = 0; p.inv.attrs = 0; + spin_lock(&fl->hlock); + if (fl->file_close == 1) { + err = EBADF; + pr_warn("ADSPRPC: fastrpc_device_release is happening, So not sending any new requests to DSP"); + spin_unlock(&fl->hlock); + goto bail; + } + spin_unlock(&fl->hlock); switch (ioctl_num) { case FASTRPC_IOCTL_INVOKE: @@ -2682,6 +2727,10 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num, VERIFY(err, 0 == copy_from_user(&p.init, param, size)); if (err) goto bail; + VERIFY(err, p.init.init.filelen >= 0 && + p.init.init.memlen >= 0); + if (err) + goto bail; VERIFY(err, 0 == fastrpc_init_process(fl, &p.init)); if (err) goto bail; @@ -2827,6 +2876,7 @@ static int fastrpc_cb_probe(struct device *dev) chan->sesscount++; debugfs_global_file = debugfs_create_file("global", 0644, debugfs_root, NULL, &debugfs_fops); + bail: return err; } @@ -2940,6 +2990,12 @@ static int fastrpc_probe(struct platform_device *pdev) } return 0; } + if (of_property_read_bool(dev->of_node, + "qcom,fastrpc-vmid-heap-shared")) + gcinfo[0].heap_vmid = AC_VM_ADSP_HEAP_SHARED; + else + gcinfo[0].heap_vmid = VMID_ADSP_Q6; + pr_info("ADSPRPC: gcinfo[0].heap_vmid %d\n", gcinfo[0].heap_vmid); me->glink = of_property_read_bool(dev->of_node, "qcom,fastrpc-glink"); VERIFY(err, !of_platform_populate(pdev->dev.of_node, fastrpc_match_table, diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index e37609abf7bf..e206d9db4d7d 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -320,6 +320,7 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) struct diag_mask_info *mask_info = NULL; struct diag_msg_mask_t *mask = NULL; struct diag_ctrl_msg_mask header; + uint8_t msg_mask_tbl_count_local; if (peripheral >= NUM_PERIPHERALS) return; @@ -360,6 +361,8 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) return; } buf = mask_info->update_buf; + msg_mask_tbl_count_local = driver->msg_mask_tbl_count; + mutex_unlock(&driver->msg_mask_lock); mutex_lock(&mask_info->lock); switch (mask_info->status) { case DIAG_CTRL_MASK_ALL_DISABLED: @@ -376,9 +379,11 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) goto err; } - for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { - if (((first < mask->ssid_first) || - (last > mask->ssid_last_tools)) && first != ALL_SSID) { + for (i = 0; i < msg_mask_tbl_count_local; i++, mask++) { + mutex_lock(&driver->msg_mask_lock); + if (((mask->ssid_first > first) || + (mask->ssid_last_tools < last)) && first != ALL_SSID) { + mutex_unlock(&driver->msg_mask_lock); continue; } @@ -419,19 +424,19 @@ proceed: if (mask_size > 0) memcpy(buf + header_len, mask->ptr, mask_size); mutex_unlock(&mask->lock); + mutex_unlock(&driver->msg_mask_lock); err = diagfwd_write(peripheral, TYPE_CNTL, buf, header_len + mask_size); if (err && err != -ENODEV) - pr_err_ratelimited("diag: Unable to send msg masks to peripheral %d\n", - peripheral); + pr_err_ratelimited("diag: Unable to send msg masks to peripheral %d, error = %d\n", + peripheral, err); if (first != ALL_SSID) break; } err: mutex_unlock(&mask_info->lock); - mutex_unlock(&driver->msg_mask_lock); } static void diag_send_time_sync_update(uint8_t peripheral) @@ -711,8 +716,8 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, } req = (struct diag_msg_build_mask_t *)src_buf; - mutex_lock(&driver->msg_mask_lock); mutex_lock(&mask_info->lock); + mutex_lock(&driver->msg_mask_lock); mask = (struct diag_msg_mask_t *)mask_info->ptr; for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { if (i < (driver->msg_mask_tbl_count - 1)) { @@ -752,6 +757,8 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, pr_err_ratelimited("diag: In %s, unable to allocate memory for msg mask ptr, mask_size: %d\n", __func__, mask_size); mutex_unlock(&mask->lock); + mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&mask_info->lock); return -ENOMEM; } mask->ptr = temp; @@ -770,8 +777,8 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, mask_info->status = DIAG_CTRL_MASK_VALID; break; } - mutex_unlock(&mask_info->lock); mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&mask_info->lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(MSG_MASKS_TYPE); @@ -826,9 +833,11 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, } req = (struct diag_msg_config_rsp_t *)src_buf; + + mutex_lock(&mask_info->lock); mutex_lock(&driver->msg_mask_lock); + mask = (struct diag_msg_mask_t *)mask_info->ptr; - mutex_lock(&mask_info->lock); mask_info->status = (req->rt_mask) ? DIAG_CTRL_MASK_ALL_ENABLED : DIAG_CTRL_MASK_ALL_DISABLED; for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { @@ -837,8 +846,8 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, mask->range * sizeof(uint32_t)); mutex_unlock(&mask->lock); } - mutex_unlock(&mask_info->lock); mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&mask_info->lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(MSG_MASKS_TYPE); @@ -1352,8 +1361,8 @@ static int diag_create_msg_mask_table(void) struct diag_msg_mask_t *mask = (struct diag_msg_mask_t *)msg_mask.ptr; struct diag_ssid_range_t range; - mutex_lock(&driver->msg_mask_lock); mutex_lock(&msg_mask.lock); + mutex_lock(&driver->msg_mask_lock); driver->msg_mask_tbl_count = MSG_MASK_TBL_CNT; for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { range.ssid_first = msg_mask_tbl[i].ssid_first; @@ -1362,8 +1371,8 @@ static int diag_create_msg_mask_table(void) if (err) break; } - mutex_unlock(&msg_mask.lock); mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&msg_mask.lock); return err; } @@ -1376,8 +1385,8 @@ static int diag_create_build_time_mask(void) struct diag_msg_mask_t *build_mask = NULL; struct diag_ssid_range_t range; - mutex_lock(&driver->msg_mask_lock); mutex_lock(&msg_bt_mask.lock); + mutex_lock(&driver->msg_mask_lock); driver->bt_msg_mask_tbl_count = MSG_MASK_TBL_CNT; build_mask = (struct diag_msg_mask_t *)msg_bt_mask.ptr; for (i = 0; i < driver->bt_msg_mask_tbl_count; i++, build_mask++) { @@ -1490,9 +1499,8 @@ static int diag_create_build_time_mask(void) } memcpy(build_mask->ptr, tbl, tbl_size); } - mutex_unlock(&msg_bt_mask.lock); mutex_unlock(&driver->msg_mask_lock); - + mutex_unlock(&msg_bt_mask.lock); return err; } @@ -1662,8 +1670,8 @@ int diag_msg_mask_copy(struct diag_mask_info *dest, struct diag_mask_info *src) err = __diag_mask_init(dest, MSG_MASK_SIZE, APPS_BUF_SIZE); if (err) return err; - mutex_lock(&driver->msg_mask_lock); mutex_lock(&dest->lock); + mutex_lock(&driver->msg_mask_lock); src_mask = (struct diag_msg_mask_t *)src->ptr; dest_mask = (struct diag_msg_mask_t *)dest->ptr; @@ -1680,9 +1688,8 @@ int diag_msg_mask_copy(struct diag_mask_info *dest, struct diag_mask_info *src) src_mask++; dest_mask++; } - mutex_unlock(&dest->lock); mutex_unlock(&driver->msg_mask_lock); - + mutex_unlock(&dest->lock); return err; } @@ -1693,15 +1700,15 @@ void diag_msg_mask_free(struct diag_mask_info *mask_info) if (!mask_info) return; - mutex_lock(&driver->msg_mask_lock); mutex_lock(&mask_info->lock); + mutex_lock(&driver->msg_mask_lock); mask = (struct diag_msg_mask_t *)mask_info->ptr; for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { kfree(mask->ptr); mask->ptr = NULL; } - mutex_unlock(&mask_info->lock); mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&mask_info->lock); __diag_mask_exit(mask_info); } @@ -1870,8 +1877,9 @@ int diag_copy_to_user_msg_mask(char __user *buf, size_t count, return -EIO; } mutex_unlock(&driver->diag_maskclear_mutex); - mutex_lock(&driver->msg_mask_lock); mutex_lock(&mask_info->lock); + mutex_lock(&driver->msg_mask_lock); + mask = (struct diag_msg_mask_t *)(mask_info->ptr); for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { ptr = mask_info->update_buf; @@ -1908,8 +1916,8 @@ int diag_copy_to_user_msg_mask(char __user *buf, size_t count, } total_len += len; } - mutex_unlock(&mask_info->lock); mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&mask_info->lock); return err ? err : total_len; } diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index cc56d68217b3..d81a39e2c637 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -642,6 +642,7 @@ struct diagchar_dev { #endif int time_sync_enabled; uint8_t uses_time_api; + struct platform_device *pdev; }; extern struct diagchar_dev *driver; diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index afaedc99a4e7..4111e599877a 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -22,6 +22,8 @@ #include <linux/sched.h> #include <linux/ratelimit.h> #include <linux/timer.h> +#include <linux/platform_device.h> +#include <linux/msm_mhi.h> #ifdef CONFIG_DIAG_OVER_USB #include <linux/usb/usbdiag.h> #endif @@ -701,6 +703,11 @@ static void diag_cmd_invalidate_polling(int change_flag) driver->polling_reg_flag = 0; list_for_each_safe(start, temp, &driver->cmd_reg_list) { item = list_entry(start, struct diag_cmd_reg_t, link); + if (&item->entry == NULL) { + pr_err("diag: In %s, unable to search command\n", + __func__); + return; + } polling = diag_cmd_chk_polling(&item->entry); if (polling == DIAG_CMD_POLLING) { driver->polling_reg_flag = 1; @@ -842,6 +849,12 @@ void diag_cmd_remove_reg_by_pid(int pid) mutex_lock(&driver->cmd_reg_mutex); list_for_each_safe(start, temp, &driver->cmd_reg_list) { item = list_entry(start, struct diag_cmd_reg_t, link); + if (&item->entry == NULL) { + pr_err("diag: In %s, unable to search command\n", + __func__); + mutex_unlock(&driver->cmd_reg_mutex); + return; + } if (item->pid == pid) { list_del(&item->link); kfree(item); @@ -860,6 +873,12 @@ void diag_cmd_remove_reg_by_proc(int proc) mutex_lock(&driver->cmd_reg_mutex); list_for_each_safe(start, temp, &driver->cmd_reg_list) { item = list_entry(start, struct diag_cmd_reg_t, link); + if (&item->entry == NULL) { + pr_err("diag: In %s, unable to search command\n", + __func__); + mutex_unlock(&driver->cmd_reg_mutex); + return; + } if (item->proc == proc) { list_del(&item->link); kfree(item); @@ -1836,14 +1855,18 @@ static int diag_ioctl_lsm_deinit(void) { int i; + mutex_lock(&driver->diagchar_mutex); for (i = 0; i < driver->num_clients; i++) if (driver->client_map[i].pid == current->tgid) break; - if (i == driver->num_clients) + if (i == driver->num_clients) { + mutex_unlock(&driver->diagchar_mutex); return -EINVAL; + } driver->data_ready[i] |= DEINIT_TYPE; + mutex_unlock(&driver->diagchar_mutex); wake_up_interruptible(&driver->wait_q); return 1; @@ -3006,6 +3029,16 @@ static int diag_user_process_apps_data(const char __user *buf, int len, return 0; } +static int check_data_ready(int index) +{ + int data_type = 0; + + mutex_lock(&driver->diagchar_mutex); + data_type = driver->data_ready[index]; + mutex_unlock(&driver->diagchar_mutex); + return data_type; +} + static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -3018,9 +3051,11 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, int write_len = 0; struct diag_md_session_t *session_info = NULL; + mutex_lock(&driver->diagchar_mutex); for (i = 0; i < driver->num_clients; i++) if (driver->client_map[i].pid == current->tgid) index = i; + mutex_unlock(&driver->diagchar_mutex); if (index == -1) { pr_err("diag: Client PID not found in table"); @@ -3030,7 +3065,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, pr_err("diag: bad address from user side\n"); return -EFAULT; } - wait_event_interruptible(driver->wait_q, driver->data_ready[index]); + wait_event_interruptible(driver->wait_q, (check_data_ready(index)) > 0); mutex_lock(&driver->diagchar_mutex); @@ -3171,11 +3206,11 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, } exit: - mutex_unlock(&driver->diagchar_mutex); if (driver->data_ready[index] & DCI_DATA_TYPE) { - mutex_lock(&driver->dci_mutex); - /* Copy the type of data being passed */ data_type = driver->data_ready[index] & DCI_DATA_TYPE; + mutex_unlock(&driver->diagchar_mutex); + /* Copy the type of data being passed */ + mutex_lock(&driver->dci_mutex); list_for_each_safe(start, temp, &driver->dci_client_list) { entry = list_entry(start, struct diag_dci_client_tbl, track); @@ -3207,6 +3242,7 @@ exit: mutex_unlock(&driver->dci_mutex); goto end; } + mutex_unlock(&driver->diagchar_mutex); end: /* * Flush any read that is currently pending on DCI data and @@ -3585,6 +3621,41 @@ static int diagchar_cleanup(void) return 0; } +static int diag_mhi_probe(struct platform_device *pdev) +{ + int ret; + + if (!mhi_is_device_ready(&pdev->dev, "qcom,mhi")) + return -EPROBE_DEFER; + driver->pdev = pdev; + ret = diag_remote_init(); + if (ret) { + diag_remote_exit(); + return ret; + } + ret = diagfwd_bridge_init(); + if (ret) { + diagfwd_bridge_exit(); + return ret; + } + pr_debug("diag: mhi device is ready\n"); + return 0; +} + +static const struct of_device_id diag_mhi_table[] = { + {.compatible = "qcom,diag-mhi"}, + {}, +}; + +static struct platform_driver diag_mhi_driver = { + .probe = diag_mhi_probe, + .driver = { + .name = "DIAG MHI Platform", + .owner = THIS_MODULE, + .of_match_table = diag_mhi_table, + }, +}; + static int __init diagchar_init(void) { dev_t dev; @@ -3674,9 +3745,6 @@ static int __init diagchar_init(void) ret = diag_masks_init(); if (ret) goto fail; - ret = diag_remote_init(); - if (ret) - goto fail; ret = diag_mux_init(); if (ret) goto fail; @@ -3713,9 +3781,7 @@ static int __init diagchar_init(void) goto fail; pr_debug("diagchar initialized now"); - ret = diagfwd_bridge_init(); - if (ret) - diagfwd_bridge_exit(); + platform_driver_register(&diag_mhi_driver); return 0; fail: @@ -3729,9 +3795,7 @@ fail: diagfwd_cntl_exit(); diag_dci_exit(); diag_masks_exit(); - diag_remote_exit(); return -1; - } static void diagchar_exit(void) diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c index 4ae2158b5a6b..74777212e4cf 100644 --- a/drivers/char/diag/diagfwd_cntl.c +++ b/drivers/char/diag/diagfwd_cntl.c @@ -67,7 +67,6 @@ void diag_cntl_channel_close(struct diagfwd_info *p_info) driver->feature[peripheral].sent_feature_mask = 0; driver->feature[peripheral].rcvd_feature_mask = 0; - flush_workqueue(driver->cntl_wq); reg_dirty |= PERIPHERAL_MASK(peripheral); diag_cmd_remove_reg_by_proc(peripheral); driver->feature[peripheral].stm_support = DISABLE_STM; diff --git a/drivers/char/diag/diagfwd_glink.c b/drivers/char/diag/diagfwd_glink.c index 03d496c2dd91..f1f8f0b2b34b 100644 --- a/drivers/char/diag/diagfwd_glink.c +++ b/drivers/char/diag/diagfwd_glink.c @@ -361,13 +361,44 @@ static void diag_glink_read_work_fn(struct work_struct *work) diagfwd_channel_read(glink_info->fwd_ctxt); } +struct diag_glink_read_work { + struct diag_glink_info *glink_info; + const void *ptr_read_done; + const void *ptr_rx_done; + size_t ptr_read_size; + struct work_struct work; +}; + +static void diag_glink_notify_rx_work_fn(struct work_struct *work) +{ + struct diag_glink_read_work *read_work = container_of(work, + struct diag_glink_read_work, work); + struct diag_glink_info *glink_info = read_work->glink_info; + + if (!glink_info || !glink_info->hdl) { + kfree(read_work); + return; + } + + diagfwd_channel_read_done(glink_info->fwd_ctxt, + (unsigned char *)(read_work->ptr_read_done), + read_work->ptr_read_size); + + glink_rx_done(glink_info->hdl, read_work->ptr_rx_done, false); + + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "diag: Rx done for packet %pK of len: %d periph: %d ch: %d\n", + read_work->ptr_rx_done, (int)read_work->ptr_read_size, + glink_info->peripheral, glink_info->type); + kfree(read_work); +} static void diag_glink_notify_rx(void *hdl, const void *priv, const void *pkt_priv, const void *ptr, size_t size) { struct diag_glink_info *glink_info = (struct diag_glink_info *)priv; - int err = 0; + struct diag_glink_read_work *read_work; if (!glink_info || !glink_info->hdl || !ptr || !pkt_priv || !hdl) return; @@ -379,12 +410,25 @@ static void diag_glink_notify_rx(void *hdl, const void *priv, "diag: received a packet %pK of len:%d from periph:%d ch:%d\n", ptr, (int)size, glink_info->peripheral, glink_info->type); + read_work = kmalloc(sizeof(*read_work), GFP_ATOMIC); + if (!read_work) { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "diag: Could not allocate read_work\n"); + glink_rx_done(glink_info->hdl, ptr, true); + return; + } + memcpy((void *)pkt_priv, ptr, size); - err = diagfwd_channel_read_done(glink_info->fwd_ctxt, - (unsigned char *)pkt_priv, size); - glink_rx_done(glink_info->hdl, ptr, false); + + read_work->glink_info = glink_info; + read_work->ptr_read_done = pkt_priv; + read_work->ptr_rx_done = ptr; + read_work->ptr_read_size = size; + INIT_WORK(&read_work->work, diag_glink_notify_rx_work_fn); + queue_work(glink_info->wq, &read_work->work); + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, - "diag: Rx done for packet %pK of len:%d periph:%d ch:%d\n", + "diag: Rx queued for packet %pK of len: %d periph: %d ch: %d\n", ptr, (int)size, glink_info->peripheral, glink_info->type); } @@ -473,6 +517,8 @@ static void diag_glink_connect_work_fn(struct work_struct *work) atomic_set(&glink_info->opened, 1); diagfwd_channel_open(glink_info->fwd_ctxt); diagfwd_late_open(glink_info->fwd_ctxt); + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "glink channel open: p: %d t: %d\n", + glink_info->peripheral, glink_info->type); } static void diag_glink_remote_disconnect_work_fn(struct work_struct *work) @@ -494,9 +540,9 @@ static void diag_glink_late_init_work_fn(struct work_struct *work) late_init_work); if (!glink_info || !glink_info->hdl) return; - DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "p: %d t: %d\n", - glink_info->peripheral, glink_info->type); diagfwd_channel_open(glink_info->fwd_ctxt); + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "glink late init p: %d t: %d\n", + glink_info->peripheral, glink_info->type); } static void diag_glink_transport_notify_state(void *handle, const void *priv, diff --git a/drivers/char/diag/diagfwd_mhi.c b/drivers/char/diag/diagfwd_mhi.c index 03133a5a89aa..edfba6bb09c9 100644 --- a/drivers/char/diag/diagfwd_mhi.c +++ b/drivers/char/diag/diagfwd_mhi.c @@ -197,7 +197,7 @@ static void mhi_buf_tbl_clear(struct diag_mhi_info *mhi_info) struct diag_mhi_buf_tbl_t *item = NULL; struct diag_mhi_ch_t *ch = NULL; - if (!mhi_info || !mhi_info->enabled) + if (!mhi_info) return; /* Clear all the pending reads */ @@ -678,7 +678,25 @@ static int diag_mhi_register_ch(int id, struct diag_mhi_ch_t *ch) atomic_set(&(ch->opened), 0); ctxt = SET_CH_CTXT(id, ch->type); ch->client_info.mhi_client_cb = mhi_notifier; - return mhi_register_channel(&ch->hdl, NULL); + ch->client_info.chan = ch->chan; + ch->client_info.dev = &driver->pdev->dev; + ch->client_info.node_name = "qcom,mhi"; + ch->client_info.user_data = (void *)(uintptr_t)ctxt; + return mhi_register_channel(&ch->hdl, &ch->client_info); +} + +static void diag_mhi_dev_exit(int dev) +{ + struct diag_mhi_info *mhi_info = NULL; + + mhi_info = &diag_mhi[dev]; + if (!mhi_info) + return; + if (mhi_info->mhi_wq) + destroy_workqueue(mhi_info->mhi_wq); + mhi_close(mhi_info->id); + if (mhi_info->mempool_init) + diagmem_exit(driver, mhi_info->mempool); } int diag_mhi_init() @@ -726,22 +744,16 @@ int diag_mhi_init() return 0; fail: - diag_mhi_exit(); + diag_mhi_dev_exit(i); return -ENOMEM; } void diag_mhi_exit() { int i; - struct diag_mhi_info *mhi_info = NULL; for (i = 0; i < NUM_MHI_DEV; i++) { - mhi_info = &diag_mhi[i]; - if (mhi_info->mhi_wq) - destroy_workqueue(mhi_info->mhi_wq); - mhi_close(mhi_info->id); - if (mhi_info->mempool_init) - diagmem_exit(driver, mhi_info->mempool); + diag_mhi_dev_exit(i); } } diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index 9ac1ad62ffe0..78b8452b19b3 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -1049,7 +1049,16 @@ void diagfwd_close_transport(uint8_t transport, uint8_t peripheral) dest_info->buf_ptr[i] = fwd_info->buf_ptr[i]; if (!check_channel_state(dest_info->ctxt)) diagfwd_late_open(dest_info); - diagfwd_cntl_open(dest_info); + + /* + * Open control channel to update masks after buffers are + * initialized for peripherals that have transport other than + * GLINK. GLINK supported peripheral mask update will + * happen after glink buffers are initialized. + */ + + if (dest_info->transport != TRANSPORT_GLINK) + diagfwd_cntl_open(dest_info); init_fn(peripheral); mutex_unlock(&driver->diagfwd_channel_mutex[peripheral]); diagfwd_queue_read(&peripheral_info[TYPE_DATA][peripheral]); @@ -1240,7 +1249,18 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info) mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); fwd_info->ch_open = 1; diagfwd_buffers_init(fwd_info); - diagfwd_write_buffers_init(fwd_info); + + /* + * Initialize buffers for glink supported + * peripherals only. Open control channel to update + * masks after buffers are initialized. + */ + if (fwd_info->transport == TRANSPORT_GLINK) { + diagfwd_write_buffers_init(fwd_info); + if (fwd_info->type == TYPE_CNTL) + diagfwd_cntl_open(fwd_info); + } + if (fwd_info && fwd_info->c_ops && fwd_info->c_ops->open) fwd_info->c_ops->open(fwd_info); for (i = 0; i < NUM_WRITE_BUFFERS; i++) { @@ -1265,6 +1285,9 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info) if (!fwd_info) return -EIO; + if (fwd_info->type == TYPE_CNTL) + flush_workqueue(driver->cntl_wq); + mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); fwd_info->ch_open = 0; if (fwd_info && fwd_info->c_ops && fwd_info->c_ops->close) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index a084a4751fa9..25372dc381d4 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -3877,6 +3877,9 @@ static void smi_recv_tasklet(unsigned long val) * because the lower layer is allowed to hold locks while calling * message delivery. */ + + rcu_read_lock(); + if (!run_to_completion) spin_lock_irqsave(&intf->xmit_msgs_lock, flags); if (intf->curr_msg == NULL && !intf->in_shutdown) { @@ -3899,6 +3902,8 @@ static void smi_recv_tasklet(unsigned long val) if (newmsg) intf->handlers->sender(intf->send_info, newmsg); + rcu_read_unlock(); + handle_new_recv_msgs(intf); } diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 0d83cfb9708f..f53e8ba2c718 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -758,6 +758,11 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, result, len, data[2]); } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || data[1] != IPMI_GET_MSG_FLAGS_CMD) { + /* + * Don't abort here, maybe it was a queued + * response to a previous command. + */ + ipmi_ssif_unlock_cond(ssif_info, flags); pr_warn(PFX "Invalid response getting flags: %x %x\n", data[0], data[1]); } else { diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 096f0cef4da1..40d400fe5bb7 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1162,10 +1162,11 @@ static int wdog_reboot_handler(struct notifier_block *this, ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { - /* Set a long timer to let the reboot happens, but - reboot if it hangs, but only if the watchdog + /* Set a long timer to let the reboot happen or + reset if it hangs, but only if the watchdog timer was already running. */ - timeout = 120; + if (timeout < 120) + timeout = 120; pretimeout = 0; ipmi_watchdog_state = WDOG_TIMEOUT_RESET; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); diff --git a/drivers/char/rdbg.c b/drivers/char/rdbg.c index 0823ed78485e..8161d77ca194 100644 --- a/drivers/char/rdbg.c +++ b/drivers/char/rdbg.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,7 +22,7 @@ #include <linux/uaccess.h> #include <linux/interrupt.h> -#define SMP2P_NUM_PROCS 8 +#define SMP2P_NUM_PROCS 16 #define MAX_RETRIES 20 #define SM_VERSION 1 @@ -146,9 +146,17 @@ static struct processor_specific_info proc_info[SMP2P_NUM_PROCS] = { {"rdbg_adsp", SMEM_LC_DEBUGGER, 16*1024}, /*ADSP*/ {0}, /*SMP2P_RESERVED_PROC_1*/ {"rdbg_wcnss", 0, 0}, /*WCNSS*/ - {0}, /*SMP2P_RESERVED_PROC_2*/ - {0}, /*SMP2P_POWER_PROC*/ - {0} /*SMP2P_REMOTE_MOCK_PROC*/ + {"rdbg_cdsp", SMEM_LC_DEBUGGER, 16*1024}, /*CDSP*/ + {NULL}, /*SMP2P_POWER_PROC*/ + {NULL}, /*SMP2P_TZ_PROC*/ + {NULL}, /*EMPTY*/ + {NULL}, /*EMPTY*/ + {NULL}, /*EMPTY*/ + {NULL}, /*EMPTY*/ + {NULL}, /*EMPTY*/ + {NULL}, /*EMPTY*/ + {NULL}, /*EMPTY*/ + {NULL} /*SMP2P_REMOTE_MOCK_PROC*/ }; static int smq_blockmap_get(struct smq_block_map *block_map, diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 252142524ff2..a0d9ac6b6cc9 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -29,33 +29,92 @@ #include "tpm.h" #include "tpm_eventlog.h" -static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES); -static LIST_HEAD(tpm_chip_list); -static DEFINE_SPINLOCK(driver_lock); +DEFINE_IDR(dev_nums_idr); +static DEFINE_MUTEX(idr_lock); struct class *tpm_class; dev_t tpm_devt; -/* - * tpm_chip_find_get - return tpm_chip for a given chip number - * @chip_num the device number for the chip +/** + * tpm_try_get_ops() - Get a ref to the tpm_chip + * @chip: Chip to ref + * + * The caller must already have some kind of locking to ensure that chip is + * valid. This function will lock the chip so that the ops member can be + * accessed safely. The locking prevents tpm_chip_unregister from + * completing, so it should not be held for long periods. + * + * Returns -ERRNO if the chip could not be got. */ -struct tpm_chip *tpm_chip_find_get(int chip_num) +int tpm_try_get_ops(struct tpm_chip *chip) { - struct tpm_chip *pos, *chip = NULL; + int rc = -EIO; - rcu_read_lock(); - list_for_each_entry_rcu(pos, &tpm_chip_list, list) { - if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num) - continue; + get_device(&chip->dev); - if (try_module_get(pos->pdev->driver->owner)) { - chip = pos; - break; - } + down_read(&chip->ops_sem); + if (!chip->ops) + goto out_lock; + + if (!try_module_get(chip->dev.parent->driver->owner)) + goto out_lock; + + return 0; +out_lock: + up_read(&chip->ops_sem); + put_device(&chip->dev); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_try_get_ops); + +/** + * tpm_put_ops() - Release a ref to the tpm_chip + * @chip: Chip to put + * + * This is the opposite pair to tpm_try_get_ops(). After this returns chip may + * be kfree'd. + */ +void tpm_put_ops(struct tpm_chip *chip) +{ + module_put(chip->dev.parent->driver->owner); + up_read(&chip->ops_sem); + put_device(&chip->dev); +} +EXPORT_SYMBOL_GPL(tpm_put_ops); + +/** + * tpm_chip_find_get() - return tpm_chip for a given chip number + * @chip_num: id to find + * + * The return'd chip has been tpm_try_get_ops'd and must be released via + * tpm_put_ops + */ +struct tpm_chip *tpm_chip_find_get(int chip_num) +{ + struct tpm_chip *chip, *res = NULL; + int chip_prev; + + mutex_lock(&idr_lock); + + if (chip_num == TPM_ANY_NUM) { + chip_num = 0; + do { + chip_prev = chip_num; + chip = idr_get_next(&dev_nums_idr, &chip_num); + if (chip && !tpm_try_get_ops(chip)) { + res = chip; + break; + } + } while (chip_prev != chip_num); + } else { + chip = idr_find_slowpath(&dev_nums_idr, chip_num); + if (chip && !tpm_try_get_ops(chip)) + res = chip; } - rcu_read_unlock(); - return chip; + + mutex_unlock(&idr_lock); + + return res; } /** @@ -68,12 +127,48 @@ static void tpm_dev_release(struct device *dev) { struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); - spin_lock(&driver_lock); - clear_bit(chip->dev_num, dev_mask); - spin_unlock(&driver_lock); + mutex_lock(&idr_lock); + idr_remove(&dev_nums_idr, chip->dev_num); + mutex_unlock(&idr_lock); + kfree(chip); } + +/** + * tpm_class_shutdown() - prepare the TPM device for loss of power. + * @dev: device to which the chip is associated. + * + * Issues a TPM2_Shutdown command prior to loss of power, as required by the + * TPM 2.0 spec. + * Then, calls bus- and device- specific shutdown code. + * + * XXX: This codepath relies on the fact that sysfs is not enabled for + * TPM2: sysfs uses an implicit lock on chip->ops, so this could race if TPM2 + * has sysfs support enabled before TPM sysfs's implicit locking is fixed. + */ +static int tpm_class_shutdown(struct device *dev) +{ + struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); + + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + down_write(&chip->ops_sem); + tpm2_shutdown(chip, TPM2_SU_CLEAR); + chip->ops = NULL; + up_write(&chip->ops_sem); + } + /* Allow bus- and device-specific code to run. Note: since chip->ops + * is NULL, more-specific shutdown code will not be able to issue TPM + * commands. + */ + if (dev->bus && dev->bus->shutdown) + dev->bus->shutdown(dev); + else if (dev->driver && dev->driver->shutdown) + dev->driver->shutdown(dev); + return 0; +} + + /** * tpmm_chip_alloc() - allocate a new struct tpm_chip instance * @dev: device to which the chip is associated @@ -88,37 +183,35 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, const struct tpm_class_ops *ops) { struct tpm_chip *chip; + int rc; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) return ERR_PTR(-ENOMEM); mutex_init(&chip->tpm_mutex); - INIT_LIST_HEAD(&chip->list); + init_rwsem(&chip->ops_sem); chip->ops = ops; - spin_lock(&driver_lock); - chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES); - spin_unlock(&driver_lock); - - if (chip->dev_num >= TPM_NUM_DEVICES) { + mutex_lock(&idr_lock); + rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL); + mutex_unlock(&idr_lock); + if (rc < 0) { dev_err(dev, "No available tpm device numbers\n"); kfree(chip); - return ERR_PTR(-ENOMEM); + return ERR_PTR(rc); } - - set_bit(chip->dev_num, dev_mask); + chip->dev_num = rc; scnprintf(chip->devname, sizeof(chip->devname), "tpm%d", chip->dev_num); - chip->pdev = dev; - dev_set_drvdata(dev, chip); chip->dev.class = tpm_class; + chip->dev.class->shutdown = tpm_class_shutdown; chip->dev.release = tpm_dev_release; - chip->dev.parent = chip->pdev; + chip->dev.parent = dev; #ifdef CONFIG_ACPI chip->dev.groups = chip->groups; #endif @@ -133,7 +226,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, device_initialize(&chip->dev); cdev_init(&chip->cdev, &tpm_fops); - chip->cdev.owner = chip->pdev->driver->owner; + chip->cdev.owner = dev->driver->owner; chip->cdev.kobj.parent = &chip->dev.kobj; devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev); @@ -167,6 +260,11 @@ static int tpm_add_char_device(struct tpm_chip *chip) return rc; } + /* Make the chip available. */ + mutex_lock(&idr_lock); + idr_replace(&dev_nums_idr, chip, chip->dev_num); + mutex_unlock(&idr_lock); + return rc; } @@ -174,6 +272,16 @@ static void tpm_del_char_device(struct tpm_chip *chip) { cdev_del(&chip->cdev); device_del(&chip->dev); + + /* Make the chip unavailable. */ + mutex_lock(&idr_lock); + idr_replace(&dev_nums_idr, NULL, chip->dev_num); + mutex_unlock(&idr_lock); + + /* Make the driver uncallable. */ + down_write(&chip->ops_sem); + chip->ops = NULL; + up_write(&chip->ops_sem); } static int tpm1_chip_register(struct tpm_chip *chip) @@ -228,17 +336,11 @@ int tpm_chip_register(struct tpm_chip *chip) if (rc) goto out_err; - /* Make the chip available. */ - spin_lock(&driver_lock); - list_add_tail_rcu(&chip->list, &tpm_chip_list); - spin_unlock(&driver_lock); - chip->flags |= TPM_CHIP_FLAG_REGISTERED; if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { - rc = __compat_only_sysfs_link_entry_to_kobj(&chip->pdev->kobj, - &chip->dev.kobj, - "ppi"); + rc = __compat_only_sysfs_link_entry_to_kobj( + &chip->dev.parent->kobj, &chip->dev.kobj, "ppi"); if (rc && rc != -ENOENT) { tpm_chip_unregister(chip); return rc; @@ -259,6 +361,9 @@ EXPORT_SYMBOL_GPL(tpm_chip_register); * Takes the chip first away from the list of available TPM chips and then * cleans up all the resources reserved by tpm_chip_register(). * + * Once this function returns the driver call backs in 'op's will not be + * running and will no longer start. + * * NOTE: This function should be only called before deinitializing chip * resources. */ @@ -267,13 +372,8 @@ void tpm_chip_unregister(struct tpm_chip *chip) if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED)) return; - spin_lock(&driver_lock); - list_del_rcu(&chip->list); - spin_unlock(&driver_lock); - synchronize_rcu(); - if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) - sysfs_remove_link(&chip->pdev->kobj, "ppi"); + sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); tpm1_chip_unregister(chip); tpm_del_char_device(chip); diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index 4f3137d9a35e..912ad30be585 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -61,7 +61,7 @@ static int tpm_open(struct inode *inode, struct file *file) * by the check of is_open variable, which is protected * by driver_lock. */ if (test_and_set_bit(0, &chip->is_open)) { - dev_dbg(chip->pdev, "Another process owns this TPM\n"); + dev_dbg(&chip->dev, "Another process owns this TPM\n"); return -EBUSY; } @@ -79,7 +79,6 @@ static int tpm_open(struct inode *inode, struct file *file) INIT_WORK(&priv->work, timeout_work); file->private_data = priv; - get_device(chip->pdev); return 0; } @@ -137,9 +136,18 @@ static ssize_t tpm_write(struct file *file, const char __user *buf, return -EFAULT; } - /* atomic tpm command send and result receive */ + /* atomic tpm command send and result receive. We only hold the ops + * lock during this period so that the tpm can be unregistered even if + * the char dev is held open. + */ + if (tpm_try_get_ops(priv->chip)) { + mutex_unlock(&priv->buffer_mutex); + return -EPIPE; + } out_size = tpm_transmit(priv->chip, priv->data_buffer, sizeof(priv->data_buffer), 0); + + tpm_put_ops(priv->chip); if (out_size < 0) { mutex_unlock(&priv->buffer_mutex); return out_size; @@ -166,7 +174,6 @@ static int tpm_release(struct inode *inode, struct file *file) file->private_data = NULL; atomic_set(&priv->data_pending, 0); clear_bit(0, &priv->chip->is_open); - put_device(priv->chip->pdev); kfree(priv); return 0; } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 17abe52e6365..aaa5fa95dede 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -343,7 +343,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, if (count == 0) return -ENODATA; if (count > bufsiz) { - dev_err(chip->pdev, + dev_err(&chip->dev, "invalid count value %x %zx\n", count, bufsiz); return -E2BIG; } @@ -353,7 +353,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, rc = chip->ops->send(chip, (u8 *) buf, count); if (rc < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "tpm_transmit: tpm_send: error %zd\n", rc); goto out; } @@ -372,7 +372,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, goto out_recv; if (chip->ops->req_canceled(chip, status)) { - dev_err(chip->pdev, "Operation Canceled\n"); + dev_err(&chip->dev, "Operation Canceled\n"); rc = -ECANCELED; goto out; } @@ -382,14 +382,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, } while (time_before(jiffies, stop)); chip->ops->cancel(chip); - dev_err(chip->pdev, "Operation Timed out\n"); + dev_err(&chip->dev, "Operation Timed out\n"); rc = -ETIME; goto out; out_recv: rc = chip->ops->recv(chip, (u8 *) buf, bufsiz); if (rc < 0) - dev_err(chip->pdev, + dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc); out: if (!(flags & TPM_TRANSMIT_UNLOCKED)) @@ -416,7 +416,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, err = be32_to_cpu(header->return_code); if (err != 0 && desc) - dev_err(chip->pdev, "A TPM error (%d) occurred %s\n", err, + dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err, desc); return err; @@ -514,7 +514,7 @@ int tpm_get_timeouts(struct tpm_chip *chip) if (rc == TPM_ERR_INVALID_POSTINIT) { /* The TPM is not started, we are the first to talk to it. Execute a startup command. */ - dev_info(chip->pdev, "Issuing TPM_STARTUP"); + dev_info(&chip->dev, "Issuing TPM_STARTUP"); if (tpm_startup(chip, TPM_ST_CLEAR)) return rc; @@ -526,7 +526,7 @@ int tpm_get_timeouts(struct tpm_chip *chip) 0, NULL); } if (rc) { - dev_err(chip->pdev, + dev_err(&chip->dev, "A TPM error (%zd) occurred attempting to determine the timeouts\n", rc); goto duration; @@ -565,7 +565,7 @@ int tpm_get_timeouts(struct tpm_chip *chip) /* Report adjusted timeouts */ if (chip->vendor.timeout_adjusted) { - dev_info(chip->pdev, + dev_info(&chip->dev, HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n", old_timeout[0], new_timeout[0], old_timeout[1], new_timeout[1], @@ -612,7 +612,7 @@ duration: chip->vendor.duration[TPM_MEDIUM] *= 1000; chip->vendor.duration[TPM_LONG] *= 1000; chip->vendor.duration_adjusted = true; - dev_info(chip->pdev, "Adjusting TPM timeout parameters."); + dev_info(&chip->dev, "Adjusting TPM timeout parameters."); } return 0; } @@ -687,7 +687,7 @@ int tpm_is_tpm2(u32 chip_num) rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0; - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } @@ -716,7 +716,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) rc = tpm2_pcr_read(chip, pcr_idx, res_buf); else rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_pcr_read); @@ -751,7 +751,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) if (chip->flags & TPM_CHIP_FLAG_TPM2) { rc = tpm2_pcr_extend(chip, pcr_idx, hash); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } @@ -761,7 +761,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0, "attempting extend a PCR value"); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_pcr_extend); @@ -802,7 +802,9 @@ int tpm_do_selftest(struct tpm_chip *chip) * around 300ms while the self test is ongoing, keep trying * until the self test duration expires. */ if (rc == -ETIME) { - dev_info(chip->pdev, HW_ERR "TPM command timed out during continue self test"); + dev_info( + &chip->dev, HW_ERR + "TPM command timed out during continue self test"); msleep(delay_msec); continue; } @@ -812,7 +814,7 @@ int tpm_do_selftest(struct tpm_chip *chip) rc = be32_to_cpu(cmd.header.out.return_code); if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) { - dev_info(chip->pdev, + dev_info(&chip->dev, "TPM is disabled/deactivated (0x%X)\n", rc); /* TPM is disabled and/or deactivated; driver can * proceed and TPM does handle commands for @@ -840,7 +842,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen) rc = tpm_transmit_cmd(chip, cmd, buflen, 0, "attempting tpm_cmd"); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_send); @@ -966,10 +968,10 @@ int tpm_pm_suspend(struct device *dev) } if (rc) - dev_err(chip->pdev, + dev_err(&chip->dev, "Error (%d) sending savestate before suspend\n", rc); else if (try > 0) - dev_warn(chip->pdev, "TPM savestate took %dms\n", + dev_warn(&chip->dev, "TPM savestate took %dms\n", try * TPM_TIMEOUT_RETRY); return rc; @@ -1023,7 +1025,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) if (chip->flags & TPM_CHIP_FLAG_TPM2) { err = tpm2_get_random(chip, out, max); - tpm_chip_put(chip); + tpm_put_ops(chip); return err; } @@ -1045,7 +1047,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) num_bytes -= recd; } while (retries-- && total < max); - tpm_chip_put(chip); + tpm_put_ops(chip); return total ? total : -EIO; } EXPORT_SYMBOL_GPL(tpm_get_random); @@ -1071,7 +1073,7 @@ int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload, rc = tpm2_seal_trusted(chip, payload, options); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_seal_trusted); @@ -1097,7 +1099,8 @@ int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload, rc = tpm2_unseal_trusted(chip, payload, options); - tpm_chip_put(chip); + tpm_put_ops(chip); + return rc; } EXPORT_SYMBOL_GPL(tpm_unseal_trusted); @@ -1124,6 +1127,7 @@ static int __init tpm_init(void) static void __exit tpm_exit(void) { + idr_destroy(&dev_nums_idr); class_destroy(tpm_class); unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES); } diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index f880856aa75e..06ac6e9657d2 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -38,6 +38,8 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, struct tpm_chip *chip = dev_get_drvdata(dev); + memset(&tpm_cmd, 0, sizeof(tpm_cmd)); + tpm_cmd.header.in = tpm_readpubek_header; err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, 0, "attempting to read the PUBEK"); @@ -284,16 +286,28 @@ static const struct attribute_group tpm_dev_group = { int tpm_sysfs_add_device(struct tpm_chip *chip) { int err; - err = sysfs_create_group(&chip->pdev->kobj, + + /* XXX: If you wish to remove this restriction, you must first update + * tpm_sysfs to explicitly lock chip->ops. + */ + if (chip->flags & TPM_CHIP_FLAG_TPM2) + return 0; + + err = sysfs_create_group(&chip->dev.parent->kobj, &tpm_dev_group); if (err) - dev_err(chip->pdev, + dev_err(&chip->dev, "failed to create sysfs attributes, %d\n", err); return err; } void tpm_sysfs_del_device(struct tpm_chip *chip) { - sysfs_remove_group(&chip->pdev->kobj, &tpm_dev_group); + /* The sysfs routines rely on an implicit tpm_try_get_ops, this + * function is called before ops is null'd and the sysfs core + * synchronizes this removal so that no callbacks are running or can + * run again + */ + sysfs_remove_group(&chip->dev.parent->kobj, &tpm_dev_group); } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 2216861f89f1..772d99b3a8e4 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -34,7 +34,7 @@ enum tpm_const { TPM_MINOR = 224, /* officially assigned */ TPM_BUFSIZE = 4096, - TPM_NUM_DEVICES = 256, + TPM_NUM_DEVICES = 65536, TPM_RETRY = 50, /* 5 seconds */ }; @@ -171,11 +171,16 @@ enum tpm_chip_flags { }; struct tpm_chip { - struct device *pdev; /* Device stuff */ struct device dev; struct cdev cdev; + /* A driver callback under ops cannot be run unless ops_sem is held + * (sometimes implicitly, eg for the sysfs code). ops becomes null + * when the driver is unregistered, see tpm_try_get_ops. + */ + struct rw_semaphore ops_sem; const struct tpm_class_ops *ops; + unsigned int flags; int dev_num; /* /dev/tpm# */ @@ -195,17 +200,10 @@ struct tpm_chip { acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; #endif /* CONFIG_ACPI */ - - struct list_head list; }; #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) -static inline void tpm_chip_put(struct tpm_chip *chip) -{ - module_put(chip->pdev->driver->owner); -} - static inline int tpm_read_index(int base, int index) { outb(index, base); @@ -497,6 +495,7 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value) extern struct class *tpm_class; extern dev_t tpm_devt; extern const struct file_operations tpm_fops; +extern struct idr dev_nums_idr; enum tpm_transmit_flags { TPM_TRANSMIT_UNLOCKED = BIT(0), @@ -517,6 +516,9 @@ extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, wait_queue_head_t *, bool); struct tpm_chip *tpm_chip_find_get(int chip_num); +__must_check int tpm_try_get_ops(struct tpm_chip *chip); +void tpm_put_ops(struct tpm_chip *chip); + extern struct tpm_chip *tpmm_chip_alloc(struct device *dev, const struct tpm_class_ops *ops); extern int tpm_chip_register(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index cb7e4f6b70ba..286bd090a488 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -570,7 +570,7 @@ static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT); if (rc) { - dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n", + dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n", handle); return; } @@ -580,7 +580,7 @@ static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "flushing context"); if (rc) - dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle, + dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle, rc); tpm_buf_destroy(&buf); @@ -753,7 +753,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) * except print the error code on a system failure. */ if (rc < 0) - dev_warn(chip->pdev, "transmit returned %d while stopping the TPM", + dev_warn(&chip->dev, "transmit returned %d while stopping the TPM", rc); } EXPORT_SYMBOL_GPL(tpm2_shutdown); @@ -820,7 +820,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full) * immediately. This is a workaround for that. */ if (rc == TPM2_RC_TESTING) { - dev_warn(chip->pdev, "Got RC_TESTING, ignoring\n"); + dev_warn(&chip->dev, "Got RC_TESTING, ignoring\n"); rc = 0; } diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index dfadad0916a1..a48a878f791d 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -49,7 +49,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) for (i = 0; i < 6; i++) { status = ioread8(chip->vendor.iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { - dev_err(chip->pdev, "error reading header\n"); + dev_err(&chip->dev, "error reading header\n"); return -EIO; } *buf++ = ioread8(chip->vendor.iobase); @@ -60,12 +60,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) size = be32_to_cpu(*native_size); if (count < size) { - dev_err(chip->pdev, + dev_err(&chip->dev, "Recv size(%d) less than available space\n", size); for (; i < size; i++) { /* clear the waiting data anyway */ status = ioread8(chip->vendor.iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { - dev_err(chip->pdev, "error reading data\n"); + dev_err(&chip->dev, "error reading data\n"); return -EIO; } } @@ -76,7 +76,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) for (; i < size; i++) { status = ioread8(chip->vendor.iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { - dev_err(chip->pdev, "error reading data\n"); + dev_err(&chip->dev, "error reading data\n"); return -EIO; } *buf++ = ioread8(chip->vendor.iobase); @@ -86,7 +86,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) status = ioread8(chip->vendor.iobase + 1); if (status & ATML_STATUS_DATA_AVAIL) { - dev_err(chip->pdev, "data available is stuck\n"); + dev_err(&chip->dev, "data available is stuck\n"); return -EIO; } @@ -97,9 +97,9 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) { int i; - dev_dbg(chip->pdev, "tpm_atml_send:\n"); + dev_dbg(&chip->dev, "tpm_atml_send:\n"); for (i = 0; i < count; i++) { - dev_dbg(chip->pdev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); + dev_dbg(&chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); iowrite8(buf[i], chip->vendor.iobase); } diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 8dfb88b9739c..dd8f0eb3170a 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -52,7 +52,7 @@ struct priv_data { static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) { struct priv_data *priv = chip->vendor.priv; - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); s32 status; priv->len = 0; @@ -62,7 +62,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) status = i2c_master_send(client, buf, len); - dev_dbg(chip->pdev, + dev_dbg(&chip->dev, "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__, (int)min_t(size_t, 64, len), buf, len, status); return status; @@ -71,7 +71,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) { struct priv_data *priv = chip->vendor.priv; - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); struct tpm_output_header *hdr = (struct tpm_output_header *)priv->buffer; u32 expected_len; @@ -88,7 +88,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) return -ENOMEM; if (priv->len >= expected_len) { - dev_dbg(chip->pdev, + dev_dbg(&chip->dev, "%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__, (int)min_t(size_t, 64, expected_len), buf, count, expected_len); @@ -97,7 +97,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) } rc = i2c_master_recv(client, buf, expected_len); - dev_dbg(chip->pdev, + dev_dbg(&chip->dev, "%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__, (int)min_t(size_t, 64, expected_len), buf, count, expected_len); @@ -106,13 +106,13 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) static void i2c_atmel_cancel(struct tpm_chip *chip) { - dev_err(chip->pdev, "TPM operation cancellation was requested, but is not supported"); + dev_err(&chip->dev, "TPM operation cancellation was requested, but is not supported"); } static u8 i2c_atmel_read_status(struct tpm_chip *chip) { struct priv_data *priv = chip->vendor.priv; - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); int rc; /* The TPM fails the I2C read until it is ready, so we do the entire @@ -125,7 +125,7 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip) /* Once the TPM has completed the command the command remains readable * until another command is issued. */ rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer)); - dev_dbg(chip->pdev, + dev_dbg(&chip->dev, "%s: sts=%d", __func__, rc); if (rc <= 0) return 0; diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 63d5d22e9e60..f2aa99e34b4b 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -446,7 +446,7 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) /* read first 10 bytes, including tag, paramsize, and result */ size = recv_data(chip, buf, TPM_HEADER_SIZE); if (size < TPM_HEADER_SIZE) { - dev_err(chip->pdev, "Unable to read header\n"); + dev_err(&chip->dev, "Unable to read header\n"); goto out; } @@ -459,14 +459,14 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) size += recv_data(chip, &buf[TPM_HEADER_SIZE], expected - TPM_HEADER_SIZE); if (size < expected) { - dev_err(chip->pdev, "Unable to read remainder of result\n"); + dev_err(&chip->dev, "Unable to read remainder of result\n"); size = -ETIME; goto out; } wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); if (status & TPM_STS_DATA_AVAIL) { /* retry? */ - dev_err(chip->pdev, "Error left over data\n"); + dev_err(&chip->dev, "Error left over data\n"); size = -EIO; goto out; } diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 847f1597fe9b..a1e1474dda30 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -96,13 +96,13 @@ static s32 i2c_nuvoton_write_buf(struct i2c_client *client, u8 offset, u8 size, /* read TPM_STS register */ static u8 i2c_nuvoton_read_status(struct tpm_chip *chip) { - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); s32 status; u8 data; status = i2c_nuvoton_read_buf(client, TPM_STS, 1, &data); if (status <= 0) { - dev_err(chip->pdev, "%s() error return %d\n", __func__, + dev_err(&chip->dev, "%s() error return %d\n", __func__, status); data = TPM_STS_ERR_VAL; } @@ -127,13 +127,13 @@ static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data) /* write commandReady to TPM_STS register */ static void i2c_nuvoton_ready(struct tpm_chip *chip) { - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); s32 status; /* this causes the current command to be aborted */ status = i2c_nuvoton_write_status(client, TPM_STS_COMMAND_READY); if (status < 0) - dev_err(chip->pdev, + dev_err(&chip->dev, "%s() fail to write TPM_STS.commandReady\n", __func__); } @@ -212,7 +212,7 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value, return 0; } while (time_before(jiffies, stop)); } - dev_err(chip->pdev, "%s(%02x, %02x) -> timeout\n", __func__, mask, + dev_err(&chip->dev, "%s(%02x, %02x) -> timeout\n", __func__, mask, value); return -ETIMEDOUT; } @@ -240,7 +240,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client, &chip->vendor.read_queue) == 0) { burst_count = i2c_nuvoton_get_burstcount(client, chip); if (burst_count < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "%s() fail to read burstCount=%d\n", __func__, burst_count); return -EIO; @@ -249,12 +249,12 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client, rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_R, bytes2read, &buf[size]); if (rc < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "%s() fail on i2c_nuvoton_read_buf()=%d\n", __func__, rc); return -EIO; } - dev_dbg(chip->pdev, "%s(%d):", __func__, bytes2read); + dev_dbg(&chip->dev, "%s(%d):", __func__, bytes2read); size += bytes2read; } @@ -264,7 +264,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client, /* Read TPM command results */ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct device *dev = chip->pdev; + struct device *dev = chip->dev.parent; struct i2c_client *client = to_i2c_client(dev); s32 rc; int expected, status, burst_count, retries, size = 0; @@ -334,7 +334,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) break; } i2c_nuvoton_ready(chip); - dev_dbg(chip->pdev, "%s() -> %d\n", __func__, size); + dev_dbg(&chip->dev, "%s() -> %d\n", __func__, size); return size; } @@ -347,7 +347,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) */ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) { - struct device *dev = chip->pdev; + struct device *dev = chip->dev.parent; struct i2c_client *client = to_i2c_client(dev); u32 ordinal; size_t count = 0; diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 6c488e635fdd..e3cf9f3545c5 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -195,9 +195,9 @@ static int wait(struct tpm_chip *chip, int wait_for_bit) } if (i == TPM_MAX_TRIES) { /* timeout occurs */ if (wait_for_bit == STAT_XFE) - dev_err(chip->pdev, "Timeout in wait(STAT_XFE)\n"); + dev_err(&chip->dev, "Timeout in wait(STAT_XFE)\n"); if (wait_for_bit == STAT_RDA) - dev_err(chip->pdev, "Timeout in wait(STAT_RDA)\n"); + dev_err(&chip->dev, "Timeout in wait(STAT_RDA)\n"); return -EIO; } return 0; @@ -220,7 +220,7 @@ static void wait_and_send(struct tpm_chip *chip, u8 sendbyte) static void tpm_wtx(struct tpm_chip *chip) { number_of_wtx++; - dev_info(chip->pdev, "Granting WTX (%02d / %02d)\n", + dev_info(&chip->dev, "Granting WTX (%02d / %02d)\n", number_of_wtx, TPM_MAX_WTX_PACKAGES); wait_and_send(chip, TPM_VL_VER); wait_and_send(chip, TPM_CTRL_WTX); @@ -231,7 +231,7 @@ static void tpm_wtx(struct tpm_chip *chip) static void tpm_wtx_abort(struct tpm_chip *chip) { - dev_info(chip->pdev, "Aborting WTX\n"); + dev_info(&chip->dev, "Aborting WTX\n"); wait_and_send(chip, TPM_VL_VER); wait_and_send(chip, TPM_CTRL_WTX_ABORT); wait_and_send(chip, 0x00); @@ -257,7 +257,7 @@ recv_begin: } if (buf[0] != TPM_VL_VER) { - dev_err(chip->pdev, + dev_err(&chip->dev, "Wrong transport protocol implementation!\n"); return -EIO; } @@ -272,7 +272,7 @@ recv_begin: } if ((size == 0x6D00) && (buf[1] == 0x80)) { - dev_err(chip->pdev, "Error handling on vendor layer!\n"); + dev_err(&chip->dev, "Error handling on vendor layer!\n"); return -EIO; } @@ -284,7 +284,7 @@ recv_begin: } if (buf[1] == TPM_CTRL_WTX) { - dev_info(chip->pdev, "WTX-package received\n"); + dev_info(&chip->dev, "WTX-package received\n"); if (number_of_wtx < TPM_MAX_WTX_PACKAGES) { tpm_wtx(chip); goto recv_begin; @@ -295,14 +295,14 @@ recv_begin: } if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) { - dev_info(chip->pdev, "WTX-abort acknowledged\n"); + dev_info(&chip->dev, "WTX-abort acknowledged\n"); return size; } if (buf[1] == TPM_CTRL_ERROR) { - dev_err(chip->pdev, "ERROR-package received:\n"); + dev_err(&chip->dev, "ERROR-package received:\n"); if (buf[4] == TPM_INF_NAK) - dev_err(chip->pdev, + dev_err(&chip->dev, "-> Negative acknowledgement" " - retransmit command!\n"); return -EIO; @@ -321,7 +321,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count) ret = empty_fifo(chip, 1); if (ret) { - dev_err(chip->pdev, "Timeout while clearing FIFO\n"); + dev_err(&chip->dev, "Timeout while clearing FIFO\n"); return -EIO; } diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 289389ecef84..766370bed60c 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -113,7 +113,7 @@ static int nsc_wait_for_ready(struct tpm_chip *chip) } while (time_before(jiffies, stop)); - dev_info(chip->pdev, "wait for ready failed\n"); + dev_info(&chip->dev, "wait for ready failed\n"); return -EBUSY; } @@ -129,12 +129,12 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) { - dev_err(chip->pdev, "F0 timeout\n"); + dev_err(&chip->dev, "F0 timeout\n"); return -EIO; } if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) { - dev_err(chip->pdev, "not in normal mode (0x%x)\n", + dev_err(&chip->dev, "not in normal mode (0x%x)\n", data); return -EIO; } @@ -143,7 +143,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) for (p = buffer; p < &buffer[count]; p++) { if (wait_for_stat (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "OBF timeout (while reading data)\n"); return -EIO; } @@ -154,11 +154,11 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) if ((data & NSC_STATUS_F0) == 0 && (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0)) { - dev_err(chip->pdev, "F0 not set\n"); + dev_err(&chip->dev, "F0 not set\n"); return -EIO; } if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) { - dev_err(chip->pdev, + dev_err(&chip->dev, "expected end of command(0x%x)\n", data); return -EIO; } @@ -189,19 +189,19 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { - dev_err(chip->pdev, "IBF timeout\n"); + dev_err(&chip->dev, "IBF timeout\n"); return -EIO; } outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND); if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) { - dev_err(chip->pdev, "IBR timeout\n"); + dev_err(&chip->dev, "IBR timeout\n"); return -EIO; } for (i = 0; i < count; i++) { if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "IBF timeout (while writing data)\n"); return -EIO; } @@ -209,7 +209,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) } if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { - dev_err(chip->pdev, "IBF timeout\n"); + dev_err(&chip->dev, "IBF timeout\n"); return -EIO; } outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index f10a107614b4..7f13221aeb30 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -293,7 +293,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) /* read first 10 bytes, including tag, paramsize, and result */ if ((size = recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) { - dev_err(chip->pdev, "Unable to read header\n"); + dev_err(&chip->dev, "Unable to read header\n"); goto out; } @@ -306,7 +306,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) if ((size += recv_data(chip, &buf[TPM_HEADER_SIZE], expected - TPM_HEADER_SIZE)) < expected) { - dev_err(chip->pdev, "Unable to read remainder of result\n"); + dev_err(&chip->dev, "Unable to read remainder of result\n"); size = -ETIME; goto out; } @@ -315,7 +315,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) &chip->vendor.int_queue, false); status = tpm_tis_status(chip); if (status & TPM_STS_DATA_AVAIL) { /* retry? */ - dev_err(chip->pdev, "Error left over data\n"); + dev_err(&chip->dev, "Error left over data\n"); size = -EIO; goto out; } @@ -401,7 +401,7 @@ static void disable_interrupts(struct tpm_chip *chip) iowrite32(intmask, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); - devm_free_irq(chip->pdev, chip->vendor.irq, chip); + devm_free_irq(&chip->dev, chip->vendor.irq, chip); chip->vendor.irq = 0; } @@ -463,7 +463,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) msleep(1); if (!priv->irq_tested) { disable_interrupts(chip); - dev_err(chip->pdev, + dev_err(&chip->dev, FW_BUG "TPM interrupt not working, polling instead\n"); } priv->irq_tested = true; @@ -533,7 +533,7 @@ static int probe_itpm(struct tpm_chip *chip) rc = tpm_tis_send_data(chip, cmd_getticks, len); if (rc == 0) { - dev_info(chip->pdev, "Detected an iTPM.\n"); + dev_info(&chip->dev, "Detected an iTPM.\n"); rc = 1; } else rc = -EFAULT; @@ -766,7 +766,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (devm_request_irq (dev, i, tis_int_probe, IRQF_SHARED, chip->devname, chip) != 0) { - dev_info(chip->pdev, + dev_info(&chip->dev, "Unable to request irq: %d for probe\n", i); continue; @@ -818,7 +818,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (devm_request_irq (dev, chip->vendor.irq, tis_int_handler, IRQF_SHARED, chip->devname, chip) != 0) { - dev_info(chip->pdev, + dev_info(&chip->dev, "Unable to request irq: %d for use\n", chip->vendor.irq); chip->vendor.irq = 0; diff --git a/drivers/clk/msm/clock-local2.c b/drivers/clk/msm/clock-local2.c index 076ead6aaf34..40d8d12cda82 100644 --- a/drivers/clk/msm/clock-local2.c +++ b/drivers/clk/msm/clock-local2.c @@ -1517,8 +1517,8 @@ static int set_rate_pixel(struct clk *clk, unsigned long rate) { struct rcg_clk *rcg = to_rcg_clk(clk); struct clk_freq_tbl *pixel_freq = rcg->current_freq; - int frac_num[] = {3, 2, 4, 1}; - int frac_den[] = {8, 9, 9, 1}; + int frac_num[] = {1, 2, 4, 3, 2}; + int frac_den[] = {1, 3, 9, 8, 9}; int delta = 100000; int i, rc; diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index 72a75873b810..eb72217b9b1c 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -397,6 +397,7 @@ struct clk_osm { u32 acd_extint1_cfg; u32 acd_autoxfer_ctl; u32 acd_debugfs_addr; + u32 acd_debugfs_addr_size; bool acd_init; bool secure_init; bool red_fsm_en; @@ -1449,6 +1450,7 @@ static int clk_osm_resources_init(struct platform_device *pdev) return -ENOMEM; } pwrcl_clk.pbases[ACD_BASE] = pbase; + pwrcl_clk.acd_debugfs_addr_size = resource_size(res); pwrcl_clk.vbases[ACD_BASE] = vbase; pwrcl_clk.acd_init = true; } else { @@ -1466,6 +1468,7 @@ static int clk_osm_resources_init(struct platform_device *pdev) return -ENOMEM; } perfcl_clk.pbases[ACD_BASE] = pbase; + perfcl_clk.acd_debugfs_addr_size = resource_size(res); perfcl_clk.vbases[ACD_BASE] = vbase; perfcl_clk.acd_init = true; } else { @@ -3015,6 +3018,11 @@ static int debugfs_get_debug_reg(void *data, u64 *val) { struct clk_osm *c = data; + if (!c->pbases[ACD_BASE]) { + pr_err("ACD base start not defined\n"); + return -EINVAL; + } + if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR) *val = readl_relaxed((char *)c->vbases[ACD_BASE] + c->acd_debugfs_addr); @@ -3027,6 +3035,11 @@ static int debugfs_set_debug_reg(void *data, u64 val) { struct clk_osm *c = data; + if (!c->pbases[ACD_BASE]) { + pr_err("ACD base start not defined\n"); + return -EINVAL; + } + if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR) clk_osm_acd_master_write_reg(c, val, c->acd_debugfs_addr); else @@ -3044,7 +3057,13 @@ static int debugfs_get_debug_reg_addr(void *data, u64 *val) { struct clk_osm *c = data; + if (!c->pbases[ACD_BASE]) { + pr_err("ACD base start not defined\n"); + return -EINVAL; + } + *val = c->acd_debugfs_addr; + return 0; } @@ -3052,7 +3071,16 @@ static int debugfs_set_debug_reg_addr(void *data, u64 val) { struct clk_osm *c = data; + if (!c->pbases[ACD_BASE]) { + pr_err("ACD base start not defined\n"); + return -EINVAL; + } + + if (val >= c->acd_debugfs_addr_size) + return -EINVAL; + c->acd_debugfs_addr = val; + return 0; } DEFINE_SIMPLE_ATTRIBUTE(debugfs_acd_debug_reg_addr_fops, diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-8998.c b/drivers/clk/msm/mdss/mdss-dsi-pll-8998.c index eb69ed35f46d..040707e58e25 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll-8998.c +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-8998.c @@ -152,8 +152,6 @@ struct dsi_pll_regs { struct dsi_pll_config { u32 ref_freq; - bool div_override; - u32 output_div; bool ignore_frac; bool disable_prescaler; bool enable_ssc; @@ -212,7 +210,6 @@ static void dsi_pll_setup_config(struct dsi_pll_8998 *pll, struct dsi_pll_config *config = &pll->pll_configuration; config->ref_freq = 19200000; - config->output_div = 1; config->dec_bits = 8; config->frac_bits = 18; config->lock_timer = 64; @@ -222,7 +219,6 @@ static void dsi_pll_setup_config(struct dsi_pll_8998 *pll, config->thresh_cycles = 32; config->refclk_cycles = 256; - config->div_override = false; config->ignore_frac = false; config->disable_prescaler = false; config->enable_ssc = rsc->ssc_en; @@ -243,54 +239,14 @@ static void dsi_pll_calc_dec_frac(struct dsi_pll_8998 *pll, { struct dsi_pll_config *config = &pll->pll_configuration; struct dsi_pll_regs *regs = &pll->reg_setup; - u64 target_freq; u64 fref = rsc->vco_ref_clk_rate; - u32 computed_output_div, div_log = 0; u64 pll_freq; u64 divider; u64 dec, dec_multiple; u32 frac; u64 multiplier; - u32 i; - - target_freq = rsc->vco_current_rate; - pr_debug("target_freq = %llu\n", target_freq); - - if (config->div_override) { - computed_output_div = config->output_div; - - /* - * Computed_output_div = 2 ^ div_log - * To get div_log from output div just get the index of the - * 1 bit in the value. - * div_log ranges from 0-3. so check the 4 lsbs - */ - - for (i = 0; i < 4; i++) { - if (computed_output_div & (1 << i)) { - div_log = i; - break; - } - } - - } else { - if (target_freq < MHZ_250) { - computed_output_div = 8; - div_log = 3; - } else if (target_freq < MHZ_500) { - computed_output_div = 4; - div_log = 2; - } else if (target_freq < MHZ_1000) { - computed_output_div = 2; - div_log = 1; - } else { - computed_output_div = 1; - div_log = 0; - } - } - pr_debug("computed_output_div = %d\n", computed_output_div); - pll_freq = target_freq * computed_output_div; + pll_freq = rsc->vco_current_rate; if (config->disable_prescaler) divider = fref; @@ -315,7 +271,6 @@ static void dsi_pll_calc_dec_frac(struct dsi_pll_8998 *pll, else regs->pll_clock_inverters = 0; - regs->pll_outdiv_rate = div_log; regs->pll_lockdet_rate = config->lock_timer; regs->decimal_div_start = dec; regs->frac_div_start_low = (frac & 0xff); @@ -478,7 +433,6 @@ static void dsi_pll_commit(struct dsi_pll_8998 *pll, MDSS_PLL_REG_W(pll_base, PLL_FRAC_DIV_START_HIGH_1, reg->frac_div_start_high); MDSS_PLL_REG_W(pll_base, PLL_PLL_LOCKDET_RATE_1, 0x40); - MDSS_PLL_REG_W(pll_base, PLL_PLL_OUTDIV_RATE, reg->pll_outdiv_rate); MDSS_PLL_REG_W(pll_base, PLL_PLL_LOCK_DELAY, 0x06); MDSS_PLL_REG_W(pll_base, PLL_CMODE, 0x10); MDSS_PLL_REG_W(pll_base, PLL_CLOCK_INVERTERS, reg->pll_clock_inverters); @@ -597,11 +551,23 @@ static int dsi_pll_enable(struct dsi_pll_vco_clk *vco) { int rc; struct mdss_pll_resources *rsc = vco->priv; + struct dsi_pll_8998 *pll = rsc->priv; + struct dsi_pll_regs *regs = &pll->reg_setup; dsi_pll_enable_pll_bias(rsc); if (rsc->slave) dsi_pll_enable_pll_bias(rsc->slave); + /* + * The PLL out dividers are fixed divider clocks and hence the + * set_div is not called during set_rate cycle of the tree. + * The outdiv rate is therefore set in the pll out mux's set_sel + * callback. But that will be called only after vco's set rate. + * Hence PLL out div value is set here before locking the PLL. + */ + MDSS_PLL_REG_W(rsc->pll_base, PLL_PLL_OUTDIV_RATE, + regs->pll_outdiv_rate); + /* Start PLL */ MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_PLL_CNTRL, 0x01); @@ -728,7 +694,9 @@ static int vco_8998_prepare(struct clk *c) static unsigned long dsi_pll_get_vco_rate(struct clk *c) { struct dsi_pll_vco_clk *vco = to_vco_clk(c); - struct mdss_pll_resources *pll = vco->priv; + struct mdss_pll_resources *rsc = vco->priv; + struct dsi_pll_8998 *pll = rsc->priv; + struct dsi_pll_regs *regs = &pll->reg_setup; int rc; u64 ref_clk = vco->ref_clk_rate; u64 vco_rate; @@ -738,27 +706,30 @@ static unsigned long dsi_pll_get_vco_rate(struct clk *c) u32 outdiv; u64 pll_freq, tmp64; - rc = mdss_pll_resource_enable(pll, true); + rc = mdss_pll_resource_enable(rsc, true); if (rc) { pr_err("failed to enable pll(%d) resource, rc=%d\n", - pll->index, rc); + rsc->index, rc); return 0; } - dec = MDSS_PLL_REG_R(pll->pll_base, PLL_DECIMAL_DIV_START_1); + dec = MDSS_PLL_REG_R(rsc->pll_base, PLL_DECIMAL_DIV_START_1); dec &= 0xFF; - frac = MDSS_PLL_REG_R(pll->pll_base, PLL_FRAC_DIV_START_LOW_1); - frac |= ((MDSS_PLL_REG_R(pll->pll_base, PLL_FRAC_DIV_START_MID_1) & + frac = MDSS_PLL_REG_R(rsc->pll_base, PLL_FRAC_DIV_START_LOW_1); + frac |= ((MDSS_PLL_REG_R(rsc->pll_base, PLL_FRAC_DIV_START_MID_1) & 0xFF) << 8); - frac |= ((MDSS_PLL_REG_R(pll->pll_base, PLL_FRAC_DIV_START_HIGH_1) & + frac |= ((MDSS_PLL_REG_R(rsc->pll_base, PLL_FRAC_DIV_START_HIGH_1) & 0x3) << 16); /* OUTDIV_1:0 field is (log(outdiv, 2)) */ - outdiv = MDSS_PLL_REG_R(pll->pll_base, PLL_PLL_OUTDIV_RATE); + outdiv = MDSS_PLL_REG_R(rsc->pll_base, PLL_PLL_OUTDIV_RATE); outdiv &= 0x3; + + regs->pll_outdiv_rate = outdiv; + outdiv = 1 << outdiv; /* @@ -776,7 +747,7 @@ static unsigned long dsi_pll_get_vco_rate(struct clk *c) pr_debug("dec=0x%x, frac=0x%x, outdiv=%d, vco=%llu\n", dec, frac, outdiv, vco_rate); - (void)mdss_pll_resource_enable(pll, false); + (void)mdss_pll_resource_enable(rsc, false); return (unsigned long)vco_rate; } @@ -930,6 +901,26 @@ static int bit_clk_set_div(struct div_clk *clk, int div) return rc; } +static int dsi_pll_out_set_mux_sel(struct mux_clk *clk, int sel) +{ + struct mdss_pll_resources *rsc = clk->priv; + struct dsi_pll_8998 *pll = rsc->priv; + struct dsi_pll_regs *regs = &pll->reg_setup; + + regs->pll_outdiv_rate = sel; + + return 0; +} + +static int dsi_pll_out_get_mux_sel(struct mux_clk *clk) +{ + struct mdss_pll_resources *rsc = clk->priv; + struct dsi_pll_8998 *pll = rsc->priv; + struct dsi_pll_regs *regs = &pll->reg_setup; + + return regs->pll_outdiv_rate; +} + static int post_vco_clk_get_div(struct div_clk *clk) { int rc; @@ -1125,52 +1116,75 @@ static struct clk_mux_ops mdss_mux_ops = { .get_mux_sel = mdss_get_mux_sel, }; +static struct clk_mux_ops mdss_pll_out_mux_ops = { + .set_mux_sel = dsi_pll_out_set_mux_sel, + .get_mux_sel = dsi_pll_out_get_mux_sel, +}; + /* * Clock tree for generating DSI byte and pixel clocks. * - * - * +---------------+ - * | vco_clk | - * +-------+-------+ - * | - * +----------------------+------------------+ - * | | | - * +-------v-------+ +-------v-------+ +-------v-------+ - * | bitclk_src | | post_vco_div1 | | post_vco_div4 | - * | DIV(1..15) | +-------+-------+ +-------+-------+ - * +-------+-------+ | | - * | +------------+ | - * +--------------------+ | | - * Shadow Path | | | | - * + +-------v-------+ +------v------+ +---v-----v------+ - * | | byteclk_src | |post_bit_div | \ post_vco_mux / - * | | DIV(8) | |DIV(1,2) | \ / - * | +-------+-------+ +------+------+ +---+------+ - * | | | | - * | | +------+ +----+ - * | +--------+ | | - * | | +----v-----v------+ - * +-v---------v----+ \ pclk_src_mux / - * \ byteclk_mux / \ / - * \ / +-----+-----+ - * +----+-----+ | Shadow Path - * | | + - * v +-----v------+ | - * dsi_byte_clk | pclk_src | | - * | DIV(1..15) | | - * +-----+------+ | - * | | - * | | - * +--------+ | - * | | - * +---v----v----+ - * \ pclk_mux / - * \ / - * +---+---+ - * | - * | - * v - * dsi_pclk + * +---------------+ + * | vco_clk | + * | | + * +-------+-------+ + * | + * | + * +-------+--------+------------------+-----------------+ + * | | | | + * +------v-------+ +------v-------+ +-------v------+ +------v-------+ + * | pll_out_div1 | | pll_out_div2 | | pll_out_div4 | | pll_out_div8 | + * | DIV(1) | | DIV(2) | | DIV(4) | | DIV(8) | + * +------+-------+ +------+-------+ +-------+------+ +------+-------+ + * | | | | + * +------------+ | +--------------+ | + * | | | +---------------------------+ + * | | | | + * +--v---v---v----v--+ + * \ pll_out_mux / + * \ / + * +------+-----+ + * | + * +---------------+-----------------+ + * | | | + * +------v-----+ +-------v-------+ +-------v-------+ + * | bitclk_src | | post_vco_div1 | | post_vco_div4 | + * | DIV(1..15) | + DIV(1) | | DIV(4) | + * +------+-----+ +-------+-------+ +-------+-------+ + * | | | + * Shadow | | +---------------------+ + * Path | +-----------------------------+ | + * + | | | + * | +---------------------------------+ | | + * | | | | | + * | +------v------=+ +------v-------+ +-v---------v----+ + * | | byteclk_src | | post_bit_div | \ post_vco_mux / + * | | DIV(8) | | DIV(1,2) | \ / + * | +------+-------+ +------+-------+ +---+------+ + * | | | | + * | | | +----------+ + * | | | | + * | | +----v-----v------+ + * +-v--------v---------+ \ pclk_src_mux / + * \ byteclk_mux / \ / + * \ / +-----+-----+ + * +------+-------+ | Shadow + * | | Path + * v +-----v------+ + + * dsi_byte_clk | pclk_src | | + * | DIV(1..15) | | + * +-----+------+ | + * | | + * +------+ | + * | | + * +---v----v----+ + * \ pclk_mux / + * \ / + * +---+---+ + * | + * | + * v + * dsi_pclk * */ @@ -1186,6 +1200,83 @@ static struct dsi_pll_vco_clk dsi0pll_vco_clk = { }, }; +static struct div_clk dsi0pll_pll_out_div1 = { + .data = { + .div = 1, + .min_div = 1, + .max_div = 1, + }, + .c = { + .parent = &dsi0pll_vco_clk.c, + .dbg_name = "dsi0pll_pll_out_div1", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_pll_out_div1.c), + } +}; + +static struct div_clk dsi0pll_pll_out_div2 = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi0pll_vco_clk.c, + .dbg_name = "dsi0pll_pll_out_div2", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_pll_out_div2.c), + } +}; + +static struct div_clk dsi0pll_pll_out_div4 = { + .data = { + .div = 4, + .min_div = 4, + .max_div = 4, + }, + .c = { + .parent = &dsi0pll_vco_clk.c, + .dbg_name = "dsi0pll_pll_out_div4", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_pll_out_div4.c), + } +}; + +static struct div_clk dsi0pll_pll_out_div8 = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi0pll_vco_clk.c, + .dbg_name = "dsi0pll_pll_out_div8", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_pll_out_div8.c), + } +}; + +static struct mux_clk dsi0pll_pll_out_mux = { + .num_parents = 4, + .parents = (struct clk_src[]) { + {&dsi0pll_pll_out_div1.c, 0}, + {&dsi0pll_pll_out_div2.c, 1}, + {&dsi0pll_pll_out_div4.c, 2}, + {&dsi0pll_pll_out_div8.c, 3}, + }, + .ops = &mdss_pll_out_mux_ops, + .c = { + .parent = &dsi0pll_pll_out_div1.c, + .dbg_name = "dsi0pll_pll_out_mux", + .ops = &clk_ops_gen_mux, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_pll_out_mux.c), + } +}; static struct div_clk dsi0pll_bitclk_src = { .data = { .div = 1, @@ -1194,7 +1285,7 @@ static struct div_clk dsi0pll_bitclk_src = { }, .ops = &clk_bitclk_src_ops, .c = { - .parent = &dsi0pll_vco_clk.c, + .parent = &dsi0pll_pll_out_mux.c, .dbg_name = "dsi0pll_bitclk_src", .ops = &clk_ops_bitclk_src_c, .flags = CLKFLAG_NO_RATE_CACHE, @@ -1210,7 +1301,7 @@ static struct div_clk dsi0pll_post_vco_div1 = { }, .ops = &clk_post_vco_div_ops, .c = { - .parent = &dsi0pll_vco_clk.c, + .parent = &dsi0pll_pll_out_mux.c, .dbg_name = "dsi0pll_post_vco_div1", .ops = &clk_ops_post_vco_div_c, .flags = CLKFLAG_NO_RATE_CACHE, @@ -1226,7 +1317,7 @@ static struct div_clk dsi0pll_post_vco_div4 = { }, .ops = &clk_post_vco_div_ops, .c = { - .parent = &dsi0pll_vco_clk.c, + .parent = &dsi0pll_pll_out_mux.c, .dbg_name = "dsi0pll_post_vco_div4", .ops = &clk_ops_post_vco_div_c, .flags = CLKFLAG_NO_RATE_CACHE, @@ -1355,6 +1446,84 @@ static struct dsi_pll_vco_clk dsi1pll_vco_clk = { }, }; +static struct div_clk dsi1pll_pll_out_div1 = { + .data = { + .div = 1, + .min_div = 1, + .max_div = 1, + }, + .c = { + .parent = &dsi1pll_vco_clk.c, + .dbg_name = "dsi1pll_pll_out_div1", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_pll_out_div1.c), + } +}; + +static struct div_clk dsi1pll_pll_out_div2 = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi1pll_vco_clk.c, + .dbg_name = "dsi1pll_pll_out_div2", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_pll_out_div2.c), + } +}; + +static struct div_clk dsi1pll_pll_out_div4 = { + .data = { + .div = 4, + .min_div = 4, + .max_div = 4, + }, + .c = { + .parent = &dsi1pll_vco_clk.c, + .dbg_name = "dsi1pll_pll_out_div4", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_pll_out_div4.c), + } +}; + +static struct div_clk dsi1pll_pll_out_div8 = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi1pll_vco_clk.c, + .dbg_name = "dsi1pll_pll_out_div8", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_pll_out_div8.c), + } +}; + +static struct mux_clk dsi1pll_pll_out_mux = { + .num_parents = 4, + .parents = (struct clk_src[]) { + {&dsi1pll_pll_out_div1.c, 0}, + {&dsi1pll_pll_out_div2.c, 1}, + {&dsi1pll_pll_out_div4.c, 2}, + {&dsi1pll_pll_out_div8.c, 3}, + }, + .ops = &mdss_pll_out_mux_ops, + .c = { + .parent = &dsi1pll_pll_out_div1.c, + .dbg_name = "dsi1pll_pll_out_mux", + .ops = &clk_ops_gen_mux, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_pll_out_mux.c), + } +}; + static struct div_clk dsi1pll_bitclk_src = { .data = { .div = 1, @@ -1363,7 +1532,7 @@ static struct div_clk dsi1pll_bitclk_src = { }, .ops = &clk_bitclk_src_ops, .c = { - .parent = &dsi1pll_vco_clk.c, + .parent = &dsi1pll_pll_out_mux.c, .dbg_name = "dsi1pll_bitclk_src", .ops = &clk_ops_bitclk_src_c, .flags = CLKFLAG_NO_RATE_CACHE, @@ -1379,7 +1548,7 @@ static struct div_clk dsi1pll_post_vco_div1 = { }, .ops = &clk_post_vco_div_ops, .c = { - .parent = &dsi1pll_vco_clk.c, + .parent = &dsi1pll_pll_out_mux.c, .dbg_name = "dsi1pll_post_vco_div1", .ops = &clk_ops_post_vco_div_c, .flags = CLKFLAG_NO_RATE_CACHE, @@ -1395,7 +1564,7 @@ static struct div_clk dsi1pll_post_vco_div4 = { }, .ops = &clk_post_vco_div_ops, .c = { - .parent = &dsi1pll_vco_clk.c, + .parent = &dsi1pll_pll_out_mux.c, .dbg_name = "dsi1pll_post_vco_div4", .ops = &clk_ops_post_vco_div_c, .flags = CLKFLAG_NO_RATE_CACHE, @@ -1523,6 +1692,11 @@ static struct clk_lookup mdss_dsi_pll0cc_8998[] = { CLK_LIST(dsi0pll_post_vco_div1), CLK_LIST(dsi0pll_post_vco_div4), CLK_LIST(dsi0pll_bitclk_src), + CLK_LIST(dsi0pll_pll_out_mux), + CLK_LIST(dsi0pll_pll_out_div8), + CLK_LIST(dsi0pll_pll_out_div4), + CLK_LIST(dsi0pll_pll_out_div2), + CLK_LIST(dsi0pll_pll_out_div1), CLK_LIST(dsi0pll_vco_clk), }; static struct clk_lookup mdss_dsi_pll1cc_8998[] = { @@ -1536,6 +1710,11 @@ static struct clk_lookup mdss_dsi_pll1cc_8998[] = { CLK_LIST(dsi1pll_post_vco_div1), CLK_LIST(dsi1pll_post_vco_div4), CLK_LIST(dsi1pll_bitclk_src), + CLK_LIST(dsi1pll_pll_out_mux), + CLK_LIST(dsi1pll_pll_out_div8), + CLK_LIST(dsi1pll_pll_out_div4), + CLK_LIST(dsi1pll_pll_out_div2), + CLK_LIST(dsi1pll_pll_out_div1), CLK_LIST(dsi1pll_vco_clk), }; @@ -1596,6 +1775,11 @@ int dsi_pll_clock_register_8998(struct platform_device *pdev, dsi0pll_post_vco_div1.priv = pll_res; dsi0pll_post_vco_div4.priv = pll_res; dsi0pll_bitclk_src.priv = pll_res; + dsi0pll_pll_out_div1.priv = pll_res; + dsi0pll_pll_out_div2.priv = pll_res; + dsi0pll_pll_out_div4.priv = pll_res; + dsi0pll_pll_out_div8.priv = pll_res; + dsi0pll_pll_out_mux.priv = pll_res; dsi0pll_vco_clk.priv = pll_res; rc = of_msm_clock_register(pdev->dev.of_node, @@ -1612,6 +1796,11 @@ int dsi_pll_clock_register_8998(struct platform_device *pdev, dsi1pll_post_vco_div1.priv = pll_res; dsi1pll_post_vco_div4.priv = pll_res; dsi1pll_bitclk_src.priv = pll_res; + dsi1pll_pll_out_div1.priv = pll_res; + dsi1pll_pll_out_div2.priv = pll_res; + dsi1pll_pll_out_div4.priv = pll_res; + dsi1pll_pll_out_div8.priv = pll_res; + dsi1pll_pll_out_mux.priv = pll_res; dsi1pll_vco_clk.priv = pll_res; rc = of_msm_clock_register(pdev->dev.of_node, diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index d99e13817a29..510a9803bd82 100644 --- a/drivers/clk/qcom/clk-cpu-osm.c +++ b/drivers/clk/qcom/clk-cpu-osm.c @@ -226,8 +226,8 @@ enum clk_osm_trace_packet_id { #define PLL_DD_D0_USER_CTL_LO 0x17916208 #define PLL_DD_D1_USER_CTL_LO 0x17816208 -#define PWRCL_EFUSE_SHIFT 0 -#define PWRCL_EFUSE_MASK 0 +#define PWRCL_EFUSE_SHIFT 29 +#define PWRCL_EFUSE_MASK 0x7 #define PERFCL_EFUSE_SHIFT 29 #define PERFCL_EFUSE_MASK 0x7 @@ -384,6 +384,7 @@ struct clk_osm { u32 acd_extint1_cfg; u32 acd_autoxfer_ctl; u32 acd_debugfs_addr; + u32 acd_debugfs_addr_size; bool acd_init; bool secure_init; bool red_fsm_en; @@ -622,18 +623,21 @@ static inline bool is_better_rate(unsigned long req, unsigned long best, return (req <= new && new < best) || (best < req && best < new); } -static long clk_osm_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int clk_osm_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { int i; unsigned long rrate = 0; + unsigned long rate = req->rate; /* * If the rate passed in is 0, return the first frequency in the * FMAX table. */ - if (!rate) - return hw->init->rate_max[0]; + if (!rate) { + req->rate = hw->init->rate_max[0]; + return 0; + } for (i = 0; i < hw->init->num_rate_max; i++) { if (is_better_rate(rate, rrate, hw->init->rate_max[i])) { @@ -643,10 +647,12 @@ static long clk_osm_round_rate(struct clk_hw *hw, unsigned long rate, } } + req->rate = rrate; + pr_debug("%s: rate %lu, rrate %ld, Rate max %ld\n", __func__, rate, rrate, hw->init->rate_max[i]); - return rrate; + return 0; } static int clk_osm_search_table(struct osm_entry *table, int entries, long rate) @@ -677,18 +683,19 @@ static int clk_osm_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_osm *cpuclk = to_clk_osm(hw); int index = 0; - unsigned long r_rate; + struct clk_rate_request req; - r_rate = clk_osm_round_rate(hw, rate, NULL); + req.rate = rate; + clk_osm_determine_rate(hw, &req); - if (rate != r_rate) { + if (rate != req.rate) { pr_err("invalid rate requested rate=%ld\n", rate); return -EINVAL; } /* Convert rate to table index */ index = clk_osm_search_table(cpuclk->osm_table, - cpuclk->num_entries, r_rate); + cpuclk->num_entries, req.rate); if (index < 0) { pr_err("cannot set cluster %u to %lu\n", cpuclk->cluster_num, rate); @@ -772,7 +779,7 @@ static unsigned long clk_osm_recalc_rate(struct clk_hw *hw, static struct clk_ops clk_ops_cpu_osm = { .enable = clk_osm_enable, .set_rate = clk_osm_set_rate, - .round_rate = clk_osm_round_rate, + .determine_rate = clk_osm_determine_rate, .list_rate = clk_osm_list_rate, .recalc_rate = clk_osm_recalc_rate, .debug_init = clk_debug_measure_add, @@ -1371,6 +1378,7 @@ static int clk_osm_resources_init(struct platform_device *pdev) return -ENOMEM; } pwrcl_clk.pbases[ACD_BASE] = pbase; + pwrcl_clk.acd_debugfs_addr_size = resource_size(res); pwrcl_clk.vbases[ACD_BASE] = vbase; pwrcl_clk.acd_init = true; } else { @@ -1388,6 +1396,7 @@ static int clk_osm_resources_init(struct platform_device *pdev) return -ENOMEM; } perfcl_clk.pbases[ACD_BASE] = pbase; + perfcl_clk.acd_debugfs_addr_size = resource_size(res); perfcl_clk.vbases[ACD_BASE] = vbase; perfcl_clk.acd_init = true; } else { @@ -2832,6 +2841,11 @@ static int debugfs_get_debug_reg(void *data, u64 *val) { struct clk_osm *c = data; + if (!c->pbases[ACD_BASE]) { + pr_err("ACD base start not defined\n"); + return -EINVAL; + } + if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR) *val = readl_relaxed((char *)c->vbases[ACD_BASE] + c->acd_debugfs_addr); @@ -2844,6 +2858,11 @@ static int debugfs_set_debug_reg(void *data, u64 val) { struct clk_osm *c = data; + if (!c->pbases[ACD_BASE]) { + pr_err("ACD base start not defined\n"); + return -EINVAL; + } + if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR) clk_osm_acd_master_write_reg(c, val, c->acd_debugfs_addr); else @@ -2861,7 +2880,13 @@ static int debugfs_get_debug_reg_addr(void *data, u64 *val) { struct clk_osm *c = data; + if (!c->pbases[ACD_BASE]) { + pr_err("ACD base start not defined\n"); + return -EINVAL; + } + *val = c->acd_debugfs_addr; + return 0; } @@ -2869,6 +2894,14 @@ static int debugfs_set_debug_reg_addr(void *data, u64 val) { struct clk_osm *c = data; + if (!c->pbases[ACD_BASE]) { + pr_err("ACD base start not defined\n"); + return -EINVAL; + } + + if (val >= c->acd_debugfs_addr_size) + return -EINVAL; + c->acd_debugfs_addr = val; return 0; } diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index b91e115462ae..abbee61c99c8 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -479,6 +479,7 @@ static void cpufreq_interactive_timer(unsigned long data) bool skip_hispeed_logic, skip_min_sample_time; bool jump_to_max_no_ts = false; bool jump_to_max = false; + bool start_hyst = true; if (!down_read_trylock(&ppol->enable_sem)) return; @@ -588,8 +589,12 @@ static void cpufreq_interactive_timer(unsigned long data) } if (now - ppol->max_freq_hyst_start_time < - tunables->max_freq_hysteresis) + tunables->max_freq_hysteresis) { + if (new_freq < ppol->policy->max && + ppol->policy->max <= tunables->hispeed_freq) + start_hyst = false; new_freq = max(tunables->hispeed_freq, new_freq); + } if (!skip_hispeed_logic && ppol->target_freq >= tunables->hispeed_freq && @@ -646,7 +651,7 @@ static void cpufreq_interactive_timer(unsigned long data) ppol->floor_validate_time = now; } - if (new_freq >= ppol->policy->max && !jump_to_max_no_ts) + if (start_hyst && new_freq >= ppol->policy->max && !jump_to_max_no_ts) ppol->max_freq_hyst_start_time = now; if (ppol->target_freq == new_freq && diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index 0dadb6332f0e..7abe908427df 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -963,7 +963,9 @@ static int atmel_sha_finup(struct ahash_request *req) ctx->flags |= SHA_FLAGS_FINUP; err1 = atmel_sha_update(req); - if (err1 == -EINPROGRESS || err1 == -EBUSY) + if (err1 == -EINPROGRESS || + (err1 == -EBUSY && (ahash_request_flags(req) & + CRYPTO_TFM_REQ_MAY_BACKLOG))) return err1; /* diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 99d5e11db194..e06cc5df30be 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -498,7 +498,7 @@ static int hash_digest_key(struct caam_hash_ctx *ctx, const u8 *key_in, ret = caam_jr_enqueue(jrdev, desc, split_key_done, &result); if (!ret) { /* in progress */ - wait_for_completion_interruptible(&result.completion); + wait_for_completion(&result.completion); ret = result.err; #ifdef DEBUG print_hex_dump(KERN_ERR, diff --git a/drivers/crypto/caam/key_gen.c b/drivers/crypto/caam/key_gen.c index e1eaf4ff9762..3ce1d5cdcbd2 100644 --- a/drivers/crypto/caam/key_gen.c +++ b/drivers/crypto/caam/key_gen.c @@ -103,7 +103,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len, ret = caam_jr_enqueue(jrdev, desc, split_key_done, &result); if (!ret) { /* in progress */ - wait_for_completion_interruptible(&result.completion); + wait_for_completion(&result.completion); ret = result.err; #ifdef DEBUG print_hex_dump(KERN_ERR, "ctx.key@"__stringify(__LINE__)": ", diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c index 7dd8d3d3f6ad..490f8d9ddb9f 100644 --- a/drivers/crypto/msm/ice.c +++ b/drivers/crypto/msm/ice.c @@ -975,7 +975,8 @@ static int qcom_ice_secure_ice_init(struct ice_device *ice_dev) static int qcom_ice_update_sec_cfg(struct ice_device *ice_dev) { - int ret = 0, scm_ret = 0; + int ret = 0; + u64 scm_ret = 0; /* scm command buffer structure */ struct qcom_scm_cmd_buf { @@ -1001,7 +1002,7 @@ static int qcom_ice_update_sec_cfg(struct ice_device *ice_dev) cbuf.device_id = ICE_TZ_DEV_ID; ret = scm_restore_sec_cfg(cbuf.device_id, cbuf.spare, &scm_ret); if (ret || scm_ret) { - pr_err("%s: failed, ret %d scm_ret %d\n", + pr_err("%s: failed, ret %d scm_ret %llu\n", __func__, ret, scm_ret); if (!ret) ret = scm_ret; @@ -1432,7 +1433,7 @@ static int qcom_ice_config_start(struct platform_device *pdev, int ret = 0; bool is_pfe = false; - if (!pdev || !req || !setting) { + if (!pdev || !req) { pr_err("%s: Invalid params passed\n", __func__); return -EINVAL; } diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c index 4ab8ca143f6c..b44f926a6ba0 100644 --- a/drivers/crypto/msm/qce50.c +++ b/drivers/crypto/msm/qce50.c @@ -2155,6 +2155,10 @@ static int _sha_complete(struct qce_device *pce_dev, int req_info) pce_sps_data = &preq_info->ce_sps; qce_callback = preq_info->qce_cb; areq = (struct ahash_request *) preq_info->areq; + if (!areq) { + pr_err("sha operation error. areq is NULL\n"); + return -ENXIO; + } qce_dma_unmap_sg(pce_dev->pdev, areq->src, preq_info->src_nents, DMA_TO_DEVICE); memcpy(digest, (char *)(&pce_sps_data->result->auth_iv[0]), @@ -2970,7 +2974,7 @@ static inline int qce_alloc_req_info(struct qce_device *pce_dev) request_index++; if (request_index >= MAX_QCE_BAM_REQ) request_index = 0; - if (xchg(&pce_dev->ce_request_info[request_index]. + if (atomic_xchg(&pce_dev->ce_request_info[request_index]. in_use, true) == false) { pce_dev->ce_request_index = request_index; return request_index; @@ -2986,7 +2990,8 @@ static inline void qce_free_req_info(struct qce_device *pce_dev, int req_info, bool is_complete) { pce_dev->ce_request_info[req_info].xfer_type = QCE_XFER_TYPE_LAST; - if (xchg(&pce_dev->ce_request_info[req_info].in_use, false) == true) { + if (atomic_xchg(&pce_dev->ce_request_info[req_info].in_use, + false) == true) { if (req_info < MAX_QCE_BAM_REQ && is_complete) atomic_dec(&pce_dev->no_of_queued_req); } else @@ -4610,7 +4615,7 @@ static int qce_dummy_req(struct qce_device *pce_dev) { int ret = 0; - if (!(xchg(&pce_dev->ce_request_info[DUMMY_REQ_INDEX]. + if (!(atomic_xchg(&pce_dev->ce_request_info[DUMMY_REQ_INDEX]. in_use, true) == false)) return -EBUSY; ret = qce_process_sha_req(pce_dev, NULL); @@ -5969,7 +5974,7 @@ void *qce_open(struct platform_device *pdev, int *rc) } for (i = 0; i < MAX_QCE_ALLOC_BAM_REQ; i++) - pce_dev->ce_request_info[i].in_use = false; + atomic_set(&pce_dev->ce_request_info[i].in_use, false); pce_dev->ce_request_index = 0; pce_dev->memsize = 10 * PAGE_SIZE * MAX_QCE_ALLOC_BAM_REQ; @@ -6133,12 +6138,13 @@ EXPORT_SYMBOL(qce_hw_support); void qce_dump_req(void *handle) { int i; + bool req_in_use; struct qce_device *pce_dev = (struct qce_device *)handle; for (i = 0; i < MAX_QCE_BAM_REQ; i++) { - pr_info("qce_dump_req %d %d\n", i, - pce_dev->ce_request_info[i].in_use); - if (pce_dev->ce_request_info[i].in_use == true) + req_in_use = atomic_read(&pce_dev->ce_request_info[i].in_use); + pr_info("qce_dump_req %d %d\n", i, req_in_use); + if (req_in_use == true) _qce_dump_descr_fifos(pce_dev, i); } } diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h index 6dba3664ff08..ab0d21da72c5 100644 --- a/drivers/crypto/msm/qce50.h +++ b/drivers/crypto/msm/qce50.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -214,7 +214,7 @@ struct ce_sps_data { }; struct ce_request_info { - bool in_use; + atomic_t in_use; bool in_prog; enum qce_xfer_type_enum xfer_type; struct ce_sps_data ce_sps; diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c index 5b364f053b1b..f38fc422b35e 100644 --- a/drivers/crypto/msm/qcrypto.c +++ b/drivers/crypto/msm/qcrypto.c @@ -3972,6 +3972,7 @@ static int _sha1_hmac_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int len) { struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(&tfm->base); + int ret = 0; memset(&sha_ctx->authkey[0], 0, SHA1_BLOCK_SIZE); if (len <= SHA1_BLOCK_SIZE) { memcpy(&sha_ctx->authkey[0], key, len); @@ -3979,16 +3980,19 @@ static int _sha1_hmac_setkey(struct crypto_ahash *tfm, const u8 *key, } else { sha_ctx->alg = QCE_HASH_SHA1; sha_ctx->diglen = SHA1_DIGEST_SIZE; - _sha_hmac_setkey(tfm, key, len); + ret = _sha_hmac_setkey(tfm, key, len); + if (ret) + pr_err("SHA1 hmac setkey failed\n"); sha_ctx->authkey_in_len = SHA1_BLOCK_SIZE; } - return 0; + return ret; } static int _sha256_hmac_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int len) { struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(&tfm->base); + int ret = 0; memset(&sha_ctx->authkey[0], 0, SHA256_BLOCK_SIZE); if (len <= SHA256_BLOCK_SIZE) { @@ -3997,11 +4001,13 @@ static int _sha256_hmac_setkey(struct crypto_ahash *tfm, const u8 *key, } else { sha_ctx->alg = QCE_HASH_SHA256; sha_ctx->diglen = SHA256_DIGEST_SIZE; - _sha_hmac_setkey(tfm, key, len); + ret = _sha_hmac_setkey(tfm, key, len); + if (ret) + pr_err("SHA256 hmac setkey failed\n"); sha_ctx->authkey_in_len = SHA256_BLOCK_SIZE; } - return 0; + return ret; } static int _sha_hmac_init_ihash(struct ahash_request *req, diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 9a8a18aafd5c..6a60936b46e0 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -804,7 +804,7 @@ static void talitos_unregister_rng(struct device *dev) * crypto alg */ #define TALITOS_CRA_PRIORITY 3000 -#define TALITOS_MAX_KEY_SIZE 96 +#define TALITOS_MAX_KEY_SIZE (AES_MAX_KEY_SIZE + SHA512_BLOCK_SIZE) #define TALITOS_MAX_IV_LENGTH 16 /* max of AES_BLOCK_SIZE, DES3_EDE_BLOCK_SIZE */ struct talitos_ctx { @@ -1388,6 +1388,11 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *cipher, { struct talitos_ctx *ctx = crypto_ablkcipher_ctx(cipher); + if (keylen > TALITOS_MAX_KEY_SIZE) { + crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + memcpy(&ctx->key, key, keylen); ctx->keylen = keylen; diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h index 690e3b4f8202..b36da3c1073f 100644 --- a/drivers/dma/ioat/hw.h +++ b/drivers/dma/ioat/hw.h @@ -64,6 +64,8 @@ #define PCI_DEVICE_ID_INTEL_IOAT_BDX8 0x6f2e #define PCI_DEVICE_ID_INTEL_IOAT_BDX9 0x6f2f +#define PCI_DEVICE_ID_INTEL_IOAT_SKX 0x2021 + #define IOAT_VER_1_2 0x12 /* Version 1.2 */ #define IOAT_VER_2_0 0x20 /* Version 2.0 */ #define IOAT_VER_3_0 0x30 /* Version 3.0 */ diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c index 4ef0c5e07912..abb75ebd65ea 100644 --- a/drivers/dma/ioat/init.c +++ b/drivers/dma/ioat/init.c @@ -105,6 +105,8 @@ static struct pci_device_id ioat_pci_tbl[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX8) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX9) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SKX) }, + /* I/OAT v3.3 platforms */ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD0) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD1) }, @@ -250,10 +252,15 @@ static bool is_bdx_ioat(struct pci_dev *pdev) } } +static inline bool is_skx_ioat(struct pci_dev *pdev) +{ + return (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_SKX) ? true : false; +} + static bool is_xeon_cb32(struct pci_dev *pdev) { return is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev) || - is_hsw_ioat(pdev) || is_bdx_ioat(pdev); + is_hsw_ioat(pdev) || is_bdx_ioat(pdev) || is_skx_ioat(pdev); } bool is_bwd_ioat(struct pci_dev *pdev) @@ -1350,6 +1357,8 @@ static int ioat_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) device->version = readb(device->reg_base + IOAT_VER_OFFSET); if (device->version >= IOAT_VER_3_0) { + if (is_skx_ioat(pdev)) + device->version = IOAT_VER_3_2; err = ioat3_dma_probe(device, ioat_dca_enabled); if (device->version >= IOAT_VER_3_3) diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c index a415edbe61b1..149ec2bd9bc6 100644 --- a/drivers/dma/ti-dma-crossbar.c +++ b/drivers/dma/ti-dma-crossbar.c @@ -146,6 +146,7 @@ static int ti_am335x_xbar_probe(struct platform_device *pdev) match = of_match_node(ti_am335x_master_match, dma_node); if (!match) { dev_err(&pdev->dev, "DMA master is not supported\n"); + of_node_put(dma_node); return -EINVAL; } @@ -310,6 +311,7 @@ static int ti_dra7_xbar_probe(struct platform_device *pdev) match = of_match_node(ti_dra7_master_match, dma_node); if (!match) { dev_err(&pdev->dev, "DMA master is not supported\n"); + of_node_put(dma_node); return -EINVAL; } diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c index 26f69fa61ba1..6eab3ea187c6 100644 --- a/drivers/esoc/esoc-mdm-4x.c +++ b/drivers/esoc/esoc-mdm-4x.c @@ -88,12 +88,10 @@ static void mdm_enable_irqs(struct mdm_ctrl *mdm) return; if (mdm->irq_mask & IRQ_ERRFATAL) { enable_irq(mdm->errfatal_irq); - irq_set_irq_wake(mdm->errfatal_irq, 1); mdm->irq_mask &= ~IRQ_ERRFATAL; } if (mdm->irq_mask & IRQ_STATUS) { enable_irq(mdm->status_irq); - irq_set_irq_wake(mdm->status_irq, 1); mdm->irq_mask &= ~IRQ_STATUS; } if (mdm->irq_mask & IRQ_PBLRDY) { @@ -107,12 +105,10 @@ static void mdm_disable_irqs(struct mdm_ctrl *mdm) if (!mdm) return; if (!(mdm->irq_mask & IRQ_ERRFATAL)) { - irq_set_irq_wake(mdm->errfatal_irq, 0); disable_irq_nosync(mdm->errfatal_irq); mdm->irq_mask |= IRQ_ERRFATAL; } if (!(mdm->irq_mask & IRQ_STATUS)) { - irq_set_irq_wake(mdm->status_irq, 0); disable_irq_nosync(mdm->status_irq); mdm->irq_mask |= IRQ_STATUS; } @@ -701,6 +697,7 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev) goto errfatal_err; } mdm->errfatal_irq = irq; + irq_set_irq_wake(mdm->errfatal_irq, 1); errfatal_err: /* status irq */ @@ -719,6 +716,7 @@ errfatal_err: goto status_err; } mdm->status_irq = irq; + irq_set_irq_wake(mdm->status_irq, 1); status_err: if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) { irq = platform_get_irq_byname(pdev, "plbrdy_irq"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 475c38fe9245..e40a6d8b0b92 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -1126,6 +1126,9 @@ static ssize_t amdgpu_ttm_vram_read(struct file *f, char __user *buf, if (size & 0x3 || *pos & 0x3) return -EINVAL; + if (*pos >= adev->mc.mc_vram_size) + return -ENXIO; + while (size) { unsigned long flags; uint32_t value; diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index a3b96d691ac9..58bf94b69186 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -330,6 +330,13 @@ static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg, return false; } + /* + * ignore out-of-order messages or messages that are part of a + * failed transaction + */ + if (!recv_hdr.somt && !msg->have_somt) + return false; + /* get length contained in this portion */ msg->curchunk_len = recv_hdr.msg_len; msg->curchunk_hdrlen = hdrlen; @@ -2163,7 +2170,7 @@ out_unlock: } EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume); -static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) +static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) { int len; u8 replyblock[32]; @@ -2178,12 +2185,12 @@ static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) replyblock, len); if (ret != len) { DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret); - return; + return false; } ret = drm_dp_sideband_msg_build(msg, replyblock, len, true); if (!ret) { DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]); - return; + return false; } replylen = msg->curchunk_len + msg->curchunk_hdrlen; @@ -2195,21 +2202,32 @@ static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply, replyblock, len); if (ret != len) { - DRM_DEBUG_KMS("failed to read a chunk\n"); + DRM_DEBUG_KMS("failed to read a chunk (len %d, ret %d)\n", + len, ret); + return false; } + ret = drm_dp_sideband_msg_build(msg, replyblock, len, false); - if (ret == false) + if (!ret) { DRM_DEBUG_KMS("failed to build sideband msg\n"); + return false; + } + curreply += len; replylen -= len; } + return true; } static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) { int ret = 0; - drm_dp_get_one_sb_msg(mgr, false); + if (!drm_dp_get_one_sb_msg(mgr, false)) { + memset(&mgr->down_rep_recv, 0, + sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } if (mgr->down_rep_recv.have_eomt) { struct drm_dp_sideband_msg_tx *txmsg; @@ -2265,7 +2283,12 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) { int ret = 0; - drm_dp_get_one_sb_msg(mgr, true); + + if (!drm_dp_get_one_sb_msg(mgr, true)) { + memset(&mgr->up_req_recv, 0, + sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } if (mgr->up_req_recv.have_eomt) { struct drm_dp_sideband_msg_req_body msg; @@ -2317,7 +2340,9 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn); } - drm_dp_put_mst_branch_device(mstb); + if (mstb) + drm_dp_put_mst_branch_device(mstb); + memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); } return ret; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 39b8e171cad5..47c1747e7ae3 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2716,6 +2716,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, #define VENDOR_BLOCK 0x03 #define SPEAKER_BLOCK 0x04 #define HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK 0x06 +#define COLORIMETRY_EXTENDED_DATA_BLOCK 0x05 #define EXTENDED_TAG 0x07 #define VIDEO_CAPABILITY_BLOCK 0x07 #define Y420_VIDEO_DATA_BLOCK 0x0E @@ -3526,11 +3527,17 @@ drm_extract_vcdb_info(struct drm_connector *connector, const u8 *db) (db[2] & (BIT(3) | BIT(2))) >> 2; connector->ce_scan_info = db[2] & (BIT(1) | BIT(0)); + connector->rgb_qs = + db[2] & BIT(6); + connector->yuv_qs = + db[2] & BIT(7); DRM_DEBUG_KMS("Scan Info (pt|it|ce): (%d|%d|%d)", (int) connector->pt_scan_info, (int) connector->it_scan_info, (int) connector->ce_scan_info); + DRM_DEBUG_KMS("rgb_quant_range_select %d", connector->rgb_qs); + DRM_DEBUG_KMS("ycc_quant_range_select %d", connector->yuv_qs); } static bool drm_edid_is_luminance_value_present( @@ -3589,6 +3596,50 @@ drm_extract_hdr_db(struct drm_connector *connector, const u8 *db) } /* + * drm_extract_colorimetry_db - Parse the HDMI colorimetry extended block + * @connector: connector corresponding to the HDMI sink + * @db: start of the HDMI colorimetry extended block + * + * Parses the HDMI colorimetry block to extract sink info for @connector. + */ +static void +drm_extract_clrmetry_db(struct drm_connector *connector, const u8 *db) +{ + + if (!db) { + DRM_ERROR("invalid db\n"); + return; + } + + /* Bit 0: xvYCC_601 */ + if (db[2] & BIT(0)) + connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_xvYCC_601; + /* Bit 0: xvYCC_709 */ + if (db[2] & BIT(1)) + connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_xvYCC_709; + /* Bit 0: sYCC_601 */ + if (db[2] & BIT(2)) + connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_sYCC_601; + /* Bit 0: ADBYCC_601 */ + if (db[2] & BIT(3)) + connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_ADBYCC_601; + /* Bit 0: ADB_RGB */ + if (db[2] & BIT(4)) + connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_ADB_RGB; + /* Bit 0: BT2020_CYCC */ + if (db[2] & BIT(5)) + connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_BT2020_CYCC; + /* Bit 0: BT2020_YCC */ + if (db[2] & BIT(6)) + connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_BT2020_YCC; + /* Bit 0: BT2020_RGB */ + if (db[2] & BIT(7)) + connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_BT2020_RGB; + + DRM_DEBUG_KMS("colorimetry fmt 0x%x\n", connector->color_enc_fmt); +} + +/* * drm_hdmi_extract_extended_blk_info - Parse the HDMI extended tag blocks * @connector: connector corresponding to the HDMI sink * @edid: handle to the EDID structure @@ -3620,6 +3671,9 @@ struct edid *edid) case HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK: drm_extract_hdr_db(connector, db); break; + case COLORIMETRY_EXTENDED_DATA_BLOCK: + drm_extract_clrmetry_db(connector, db); + break; default: break; } diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index dbc198b00792..cb3b25ddd0da 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -98,3 +98,13 @@ config DRM_SDE_HDMI default y help Choose this option if HDMI connector support is needed in SDE driver. + +config DRM_SDE_EVTLOG_DEBUG + bool "Enable event logging in MSM DRM" + depends on DRM_MSM + help + The SDE DRM debugging provides support to enable display debugging + features to: dump SDE registers during driver errors, panic + driver during fatal errors and enable some display-driver logging + into an internal buffer (this avoids logging overhead). + diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 84125b3d1f95..678b2178cb69 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -48,6 +48,8 @@ msm_drm-y := \ sde/sde_backlight.o \ sde/sde_color_processing.o \ sde/sde_vbif.o \ + sde/sde_splash.o \ + sde_dbg.o \ sde_dbg_evtlog.o \ sde_io_util.o \ dba_bridge.o \ diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index e24827590b7c..c085e173232b 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -409,8 +409,8 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) gpu->funcs->pm_resume(gpu); seq_printf(m, "status: %08x\n", gpu_read(gpu, REG_A3XX_RBBM_STATUS)); - gpu->funcs->pm_suspend(gpu); adreno_show(gpu, m); + gpu->funcs->pm_suspend(gpu); } #endif diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c index b612c9a18faf..624c2a87d593 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c @@ -447,9 +447,9 @@ static void a4xx_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "status: %08x\n", gpu_read(gpu, REG_A4XX_RBBM_STATUS)); - gpu->funcs->pm_suspend(gpu); adreno_show(gpu, m); + gpu->funcs->pm_suspend(gpu); } #endif diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 37323e962c2c..765c1c087c76 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -15,6 +15,7 @@ #include "msm_iommu.h" #include "msm_trace.h" #include "a5xx_gpu.h" +#include <linux/clk/msm-clk.h> #define SECURE_VA_START 0xc0000000 #define SECURE_VA_SIZE SZ_256M @@ -375,6 +376,7 @@ static const struct { void a5xx_set_hwcg(struct msm_gpu *gpu, bool state) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); unsigned int i; for (i = 0; i < ARRAY_SIZE(a5xx_hwcg); i++) @@ -391,6 +393,11 @@ void a5xx_set_hwcg(struct msm_gpu *gpu, bool state) gpu_write(gpu, REG_A5XX_RBBM_CLOCK_CNTL, state ? 0xAAA8AA00 : 0); gpu_write(gpu, REG_A5XX_RBBM_ISDB_CNT, state ? 0x182 : 0x180); + + if (state) + set_bit(A5XX_HWCG_ENABLED, &a5xx_gpu->flags); + else + clear_bit(A5XX_HWCG_ENABLED, &a5xx_gpu->flags); } static int a5xx_me_init(struct msm_gpu *gpu) @@ -1163,11 +1170,26 @@ static int a5xx_pm_resume(struct msm_gpu *gpu) { int ret; + /* + * Between suspend/resumes the GPU clocks need to be turned off + * but not a complete power down, typically between frames. Set the + * memory retention flags on the GPU core clock to retain memory + * across clock toggles. + */ + if (gpu->core_clk) { + clk_set_flags(gpu->core_clk, CLKFLAG_RETAIN_PERIPH); + clk_set_flags(gpu->core_clk, CLKFLAG_RETAIN_MEM); + } + /* Turn on the core power */ ret = msm_gpu_pm_resume(gpu); if (ret) return ret; + /* If we are already up, don't mess with what works */ + if (gpu->active_cnt > 1) + return 0; + /* Turn the RBCCU domain first to limit the chances of voltage droop */ gpu_write(gpu, REG_A5XX_GPMU_RBCCU_POWER_CNTL, 0x778000); @@ -1198,22 +1220,33 @@ static int a5xx_pm_suspend(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - /* Clear the VBIF pipe before shutting down */ + /* Turn off the memory retention flag when not necessary */ + if (gpu->core_clk) { + clk_set_flags(gpu->core_clk, CLKFLAG_NORETAIN_PERIPH); + clk_set_flags(gpu->core_clk, CLKFLAG_NORETAIN_MEM); + } - gpu_write(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL0, 0xF); - spin_until((gpu_read(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL1) & 0xF) == 0xF); + /* Only do this next bit if we are about to go down */ + if (gpu->active_cnt == 1) { + /* Clear the VBIF pipe before shutting down */ - gpu_write(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL0, 0); + gpu_write(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL0, 0xF); + spin_until((gpu_read(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL1) & 0xF) + == 0xF); - /* - * Reset the VBIF before power collapse to avoid issue with FIFO - * entries - */ + gpu_write(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL0, 0); - if (adreno_is_a530(adreno_gpu)) { - /* These only need to be done for A530 */ - gpu_write(gpu, REG_A5XX_RBBM_BLOCK_SW_RESET_CMD, 0x003C0000); - gpu_write(gpu, REG_A5XX_RBBM_BLOCK_SW_RESET_CMD, 0x00000000); + /* + * Reset the VBIF before power collapse to avoid issue with FIFO + * entries + */ + if (adreno_is_a530(adreno_gpu)) { + /* These only need to be done for A530 */ + gpu_write(gpu, REG_A5XX_RBBM_BLOCK_SW_RESET_CMD, + 0x003C0000); + gpu_write(gpu, REG_A5XX_RBBM_BLOCK_SW_RESET_CMD, + 0x00000000); + } } return msm_gpu_pm_suspend(gpu); @@ -1233,13 +1266,29 @@ static int a5xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value) #ifdef CONFIG_DEBUG_FS static void a5xx_show(struct msm_gpu *gpu, struct seq_file *m) { + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); + bool enabled = test_bit(A5XX_HWCG_ENABLED, &a5xx_gpu->flags); + gpu->funcs->pm_resume(gpu); seq_printf(m, "status: %08x\n", gpu_read(gpu, REG_A5XX_RBBM_STATUS)); - gpu->funcs->pm_suspend(gpu); + + /* + * Temporarily disable hardware clock gating before going into + * adreno_show to avoid issues while reading the registers + */ + + if (enabled) + a5xx_set_hwcg(gpu, false); adreno_show(gpu, m); + + if (enabled) + a5xx_set_hwcg(gpu, true); + + gpu->funcs->pm_suspend(gpu); } #endif diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h index f8b00982fe86..c30b65785ab6 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h @@ -23,6 +23,7 @@ enum { A5XX_ZAP_SHADER_LOADED = 1, + A5XX_HWCG_ENABLED = 2, }; struct a5xx_gpu { @@ -69,6 +70,8 @@ struct a5xx_gpu { * PREEMPT_NONE - no preemption in progress. Next state START. * PREEMPT_START - The trigger is evaulating if preemption is possible. Next * states: TRIGGERED, NONE + * PREEMPT_ABORT - An intermediate state before moving back to NONE. Next + * state: NONE. * PREEMPT_TRIGGERED: A preemption has been executed on the hardware. Next * states: FAULTED, PENDING * PREEMPT_FAULTED: A preemption timed out (never completed). This will trigger @@ -80,6 +83,7 @@ struct a5xx_gpu { enum preempt_state { PREEMPT_NONE = 0, PREEMPT_START, + PREEMPT_ABORT, PREEMPT_TRIGGERED, PREEMPT_FAULTED, PREEMPT_PENDING, @@ -183,7 +187,10 @@ int a5xx_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot); /* Return true if we are in a preempt state */ static inline bool a5xx_in_preempt(struct a5xx_gpu *a5xx_gpu) { - return !(atomic_read(&a5xx_gpu->preempt_state) == PREEMPT_NONE); + int preempt_state = atomic_read(&a5xx_gpu->preempt_state); + + return !(preempt_state == PREEMPT_NONE || + preempt_state == PREEMPT_ABORT); } int a5xx_counters_init(struct adreno_gpu *adreno_gpu); diff --git a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c index 6ab3ba076c2f..44d4ca35fa09 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c @@ -128,9 +128,20 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu) * one do nothing except to update the wptr to the latest and greatest */ if (!ring || (a5xx_gpu->cur_ring == ring)) { - update_wptr(gpu, ring); - - /* Set the state back to NONE */ + /* + * Its possible that while a preemption request is in progress + * from an irq context, a user context trying to submit might + * fail to update the write pointer, because it determines + * that the preempt state is not PREEMPT_NONE. + * + * Close the race by introducing an intermediate + * state PREEMPT_ABORT to let the submit path + * know that the ringbuffer is not going to change + * and can safely update the write pointer. + */ + + set_preempt_state(a5xx_gpu, PREEMPT_ABORT); + update_wptr(gpu, a5xx_gpu->cur_ring); set_preempt_state(a5xx_gpu, PREEMPT_NONE); return; } diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index a498a60cd52d..4e4709d6172f 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -173,6 +173,9 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev) ret = gpu->funcs->hw_init(gpu); if (ret) { dev_err(dev->dev, "gpu hw init failed: %d\n", ret); + mutex_lock(&dev->struct_mutex); + gpu->funcs->pm_suspend(gpu); + mutex_unlock(&dev->struct_mutex); gpu->funcs->destroy(gpu); gpu = NULL; } else { diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 2273b06b59a6..04e0056f2a49 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -297,8 +297,6 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "rb wptr: %d\n", get_wptr(ring)); } - gpu->funcs->pm_resume(gpu); - /* dump these out in a form that can be parsed by demsm: */ seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name); for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) { @@ -311,8 +309,6 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); } } - - gpu->funcs->pm_suspend(gpu); } #endif diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 1b295949122b..c98f4511d644 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -1253,6 +1253,13 @@ static int _sde_hdmi_hpd_enable(struct sde_hdmi *sde_hdmi) uint32_t hpd_ctrl; int i, ret; unsigned long flags; + struct drm_connector *connector; + struct msm_drm_private *priv; + struct sde_kms *sde_kms; + + connector = hdmi->connector; + priv = connector->dev->dev_private; + sde_kms = to_sde_kms(priv->kms); for (i = 0; i < config->hpd_reg_cnt; i++) { ret = regulator_enable(hdmi->hpd_regs[i]); @@ -1292,9 +1299,11 @@ static int _sde_hdmi_hpd_enable(struct sde_hdmi *sde_hdmi) } } - sde_hdmi_set_mode(hdmi, false); - _sde_hdmi_phy_reset(hdmi); - sde_hdmi_set_mode(hdmi, true); + if (!sde_kms->splash_info.handoff) { + sde_hdmi_set_mode(hdmi, false); + _sde_hdmi_phy_reset(hdmi); + sde_hdmi_set_mode(hdmi, true); + } hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b); @@ -1961,6 +1970,147 @@ enable_packet_control: hdmi_write(hdmi, HDMI_GEN_PKT_CTRL, packet_control); } +static void sde_hdmi_update_colorimetry(struct sde_hdmi *display, + bool use_bt2020) +{ + struct hdmi *hdmi; + struct drm_connector *connector; + bool mode_is_yuv = false; + struct drm_display_mode *mode; + u32 mode_fmt_flags = 0; + u8 checksum; + u32 avi_info0 = 0; + u32 avi_info1 = 0; + u8 avi_iframe[HDMI_AVI_INFOFRAME_BUFFER_SIZE] = {0}; + u8 *avi_frame = &avi_iframe[HDMI_INFOFRAME_HEADER_SIZE]; + struct hdmi_avi_infoframe info; + + if (!display) { + SDE_ERROR("invalid input\n"); + return; + } + + hdmi = display->ctrl.ctrl; + + if (!hdmi) { + SDE_ERROR("invalid input\n"); + return; + } + + connector = display->ctrl.ctrl->connector; + + if (!connector) { + SDE_ERROR("invalid input\n"); + return; + } + + if (!connector->hdr_supported) { + SDE_DEBUG("HDR is not supported\n"); + return; + } + + /* If sink doesn't support BT2020, just return */ + if (!(connector->color_enc_fmt & DRM_EDID_COLORIMETRY_BT2020_YCC) || + !(connector->color_enc_fmt & DRM_EDID_COLORIMETRY_BT2020_RGB)) { + SDE_DEBUG("BT2020 colorimetry is not supported\n"); + return; + } + + /* If there is no change in colorimetry, just return */ + if (use_bt2020 && display->bt2020_colorimetry) + return; + else if (!use_bt2020 && !display->bt2020_colorimetry) + return; + + mode = &display->mode; + /* Cache the format flags before clearing */ + mode_fmt_flags = mode->flags; + /** + * Clear the RGB/YUV format flags before calling upstream API + * as the API also compares the flags and then returns a mode + */ + mode->flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK; + drm_hdmi_avi_infoframe_from_display_mode(&info, mode); + /* Restore the format flags */ + mode->flags = mode_fmt_flags; + + /* Mode should only support YUV and not both to set the flag */ + if ((mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) + && !(mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_RGB444)) { + mode_is_yuv = true; + } + + + if (!display->bt2020_colorimetry && use_bt2020) { + /** + * 1. Update colorimetry to use extended + * 2. Change extended to use BT2020 + * 3. Change colorspace based on mode + * 4. Use limited as BT2020 is always limited + */ + info.colorimetry = SDE_HDMI_USE_EXTENDED_COLORIMETRY; + info.extended_colorimetry = SDE_HDMI_BT2020_COLORIMETRY; + if (mode_is_yuv) + info.colorspace = HDMI_COLORSPACE_YUV420; + if (connector->yuv_qs) + info.ycc_quantization_range = + HDMI_YCC_QUANTIZATION_RANGE_LIMITED; + } else if (display->bt2020_colorimetry && !use_bt2020) { + /** + * 1. Update colorimetry to non-extended + * 2. Change colorspace based on mode + * 3. Restore quantization to full if QS + * is enabled + */ + info.colorimetry = SDE_HDMI_DEFAULT_COLORIMETRY; + if (mode_is_yuv) + info.colorspace = HDMI_COLORSPACE_YUV420; + if (connector->yuv_qs) + info.ycc_quantization_range = + HDMI_YCC_QUANTIZATION_RANGE_FULL; + } + + hdmi_avi_infoframe_pack(&info, avi_iframe, sizeof(avi_iframe)); + checksum = avi_iframe[HDMI_INFOFRAME_HEADER_SIZE - 1]; + avi_info0 = checksum | + LEFT_SHIFT_BYTE(avi_frame[0]) | + LEFT_SHIFT_WORD(avi_frame[1]) | + LEFT_SHIFT_24BITS(avi_frame[2]); + + avi_info1 = avi_frame[3] | + LEFT_SHIFT_BYTE(avi_frame[4]) | + LEFT_SHIFT_WORD(avi_frame[5]) | + LEFT_SHIFT_24BITS(avi_frame[6]); + + hdmi_write(hdmi, REG_HDMI_AVI_INFO(0), avi_info0); + hdmi_write(hdmi, REG_HDMI_AVI_INFO(1), avi_info1); + display->bt2020_colorimetry = use_bt2020; +} + +static void sde_hdmi_clear_hdr_infoframe(struct sde_hdmi *display) +{ + struct hdmi *hdmi; + struct drm_connector *connector; + u32 packet_control = 0; + + if (!display) { + SDE_ERROR("invalid input\n"); + return; + } + + hdmi = display->ctrl.ctrl; + connector = display->ctrl.ctrl->connector; + + if (!hdmi || !connector) { + SDE_ERROR("invalid input\n"); + return; + } + + packet_control = hdmi_read(hdmi, HDMI_GEN_PKT_CTRL); + packet_control &= ~HDMI_GEN_PKT_CTRL_CLR_MASK; + hdmi_write(hdmi, HDMI_GEN_PKT_CTRL, packet_control); +} + int sde_hdmi_set_property(struct drm_connector *connector, struct drm_connector_state *state, int property_index, @@ -2305,19 +2455,110 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector, void *display, struct msm_display_kickoff_params *params) { + struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; + struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl; + struct drm_msm_ext_panel_hdr_metadata *hdr_meta; + u8 hdr_op; - if (!connector || !display || !params) { + if (!connector || !display || !params || + !params->hdr_ctrl) { pr_err("Invalid params\n"); return -EINVAL; } - if (connector->hdr_supported) - sde_hdmi_panel_set_hdr_infoframe(display, - params->hdr_metadata); + hdr_ctrl = params->hdr_ctrl; + hdr_meta = &hdr_ctrl->hdr_meta; + + if (!hdr_meta) { + SDE_ERROR("Invalid params\n"); + return -EINVAL; + } + + hdr_op = sde_hdmi_hdr_get_ops(hdmi_display->curr_hdr_state, + hdr_ctrl->hdr_state); + + if (hdr_op == HDR_SEND_INFO) { + if (connector->hdr_supported) + sde_hdmi_panel_set_hdr_infoframe(display, + &hdr_ctrl->hdr_meta); + if (hdr_meta->eotf) + sde_hdmi_update_colorimetry(hdmi_display, + true); + else + sde_hdmi_update_colorimetry(hdmi_display, + false); + } else if (hdr_op == HDR_CLEAR_INFO) + sde_hdmi_clear_hdr_infoframe(display); + + hdmi_display->curr_hdr_state = hdr_ctrl->hdr_state; return 0; } +bool sde_hdmi_mode_needs_full_range(void *display) +{ + struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; + struct drm_display_mode *mode; + u32 mode_fmt_flags = 0; + u32 cea_mode; + + if (!hdmi_display) { + SDE_ERROR("invalid input\n"); + return false; + } + + mode = &hdmi_display->mode; + /* Cache the format flags before clearing */ + mode_fmt_flags = mode->flags; + /** + * Clear the RGB/YUV format flags before calling upstream API + * as the API also compares the flags and then returns a mode + */ + mode->flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK; + cea_mode = drm_match_cea_mode(mode); + /* Restore the format flags */ + mode->flags = mode_fmt_flags; + + if (cea_mode > SDE_HDMI_VIC_640x480) + return false; + + return true; +} + +enum sde_csc_type sde_hdmi_get_csc_type(struct drm_connector *conn, + void *display) +{ + struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; + struct sde_connector_state *c_state; + struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl; + struct drm_msm_ext_panel_hdr_metadata *hdr_meta; + + if (!hdmi_display || !conn) { + SDE_ERROR("invalid input\n"); + goto error; + } + + c_state = to_sde_connector_state(conn->state); + + if (!c_state) { + SDE_ERROR("invalid input\n"); + goto error; + } + + hdr_ctrl = &c_state->hdr_ctrl; + hdr_meta = &hdr_ctrl->hdr_meta; + + if ((hdr_ctrl->hdr_state == HDR_ENABLE) + && (hdr_meta->eotf != 0)) + return SDE_CSC_RGB2YUV_2020L; + else if (sde_hdmi_mode_needs_full_range(hdmi_display) + || conn->yuv_qs) + return SDE_CSC_RGB2YUV_601FR; + +error: + return SDE_CSC_RGB2YUV_601L; +} + int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) { struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; @@ -2826,6 +3067,7 @@ int sde_hdmi_drm_init(struct sde_hdmi *display, struct drm_encoder *enc) struct msm_drm_private *priv = NULL; struct hdmi *hdmi; struct platform_device *pdev; + struct sde_kms *sde_kms; DBG(""); if (!display || !display->drm_dev || !enc) { @@ -2884,6 +3126,19 @@ int sde_hdmi_drm_init(struct sde_hdmi *display, struct drm_encoder *enc) enc->bridge = hdmi->bridge; priv->bridges[priv->num_bridges++] = hdmi->bridge; + /* + * After initialising HDMI bridge, we need to check + * whether the early display is enabled for HDMI. + * If yes, we need to increase refcount of hdmi power + * clocks. This can skip the clock disabling operation in + * clock_late_init when finding clk.count == 1. + */ + sde_kms = to_sde_kms(priv->kms); + if (sde_kms->splash_info.handoff) { + sde_hdmi_bridge_power_on(hdmi->bridge); + hdmi->power_on = true; + } + mutex_unlock(&display->display_lock); return 0; diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 743d34d05e57..672a9f188d27 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -147,6 +147,7 @@ struct sde_hdmi { u32 hdcp22_present; u8 hdcp_status; u32 enc_lvl; + u8 curr_hdr_state; bool auth_state; bool sink_hdcp22_support; bool src_hdcp22_support; @@ -168,6 +169,7 @@ struct sde_hdmi { bool pll_update_enable; bool dc_enable; bool dc_feature_supported; + bool bt2020_colorimetry; struct delayed_work hdcp_cb_work; struct dss_io_data io[HDMI_TX_MAX_IO]; @@ -198,6 +200,17 @@ enum hdmi_tx_scdc_access_type { #define HDMI_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO 2 #define HDMI_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO 1 +#define HDMI_GEN_PKT_CTRL_CLR_MASK 0x7 + +/* for AVI program */ +#define HDMI_AVI_INFOFRAME_BUFFER_SIZE \ + (HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE) +#define HDMI_VS_INFOFRAME_BUFFER_SIZE (HDMI_INFOFRAME_HEADER_SIZE + 6) + +#define LEFT_SHIFT_BYTE(x) ((x) << 8) +#define LEFT_SHIFT_WORD(x) ((x) << 16) +#define LEFT_SHIFT_24BITS(x) ((x) << 24) + /* Maximum pixel clock rates for hdmi tx */ #define HDMI_DEFAULT_MAX_PCLK_RATE 148500 #define HDMI_TX_3_MAX_PCLK_RATE 297000 @@ -354,6 +367,13 @@ int sde_hdmi_set_property(struct drm_connector *connector, int property_index, uint64_t value, void *display); +/** + * sde_hdmi_bridge_power_on -- A wrapper of _sde_hdmi_bridge_power_on. + * @bridge: Handle to the drm bridge. + * + * Return: void. + */ +void sde_hdmi_bridge_power_on(struct drm_bridge *bridge); /** * sde_hdmi_get_property() - get the connector properties @@ -472,6 +492,23 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector, void *display, struct msm_display_kickoff_params *params); +/* + * sde_hdmi_mode_needs_full_range - does mode need full range + * quantization + * @display: Pointer to private display structure + * Returns: true or false based on mode + */ +bool sde_hdmi_mode_needs_full_range(void *display); + +/* + * sde_hdmi_get_csc_type - returns the CSC type to be + * used based on state of HDR playback + * @conn: Pointer to DRM connector + * @display: Pointer to private display structure + * Returns: true or false based on mode + */ +enum sde_csc_type sde_hdmi_get_csc_type(struct drm_connector *conn, + void *display); #else /*#ifdef CONFIG_DRM_SDE_HDMI*/ static inline u32 sde_hdmi_get_num_of_displays(void) @@ -586,5 +623,16 @@ static inline int sde_hdmi_set_property(struct drm_connector *connector, return 0; } +static inline bool sde_hdmi_mode_needs_full_range(void *display) +{ + return false; +} + +enum sde_csc_type sde_hdmi_get_csc_type(struct drm_connector *conn, + void *display) +{ + return 0; +} + #endif /*#else of CONFIG_DRM_SDE_HDMI*/ #endif /* _SDE_HDMI_H_ */ diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index a4c3c2e7ce46..e6b6d15b5fb7 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -99,17 +99,10 @@ struct sde_hdmi_bridge { #define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200 -/* for AVI program */ -#define HDMI_AVI_INFOFRAME_BUFFER_SIZE \ - (HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE) -#define HDMI_VS_INFOFRAME_BUFFER_SIZE (HDMI_INFOFRAME_HEADER_SIZE + 6) #define HDMI_SPD_INFOFRAME_BUFFER_SIZE \ (HDMI_INFOFRAME_HEADER_SIZE + HDMI_SPD_INFOFRAME_SIZE) #define HDMI_DEFAULT_VENDOR_NAME "unknown" #define HDMI_DEFAULT_PRODUCT_NAME "msm" -#define LEFT_SHIFT_BYTE(x) ((x) << 8) -#define LEFT_SHIFT_WORD(x) ((x) << 16) -#define LEFT_SHIFT_24BITS(x) ((x) << 24) #define HDMI_AVI_IFRAME_LINE_NUMBER 1 #define HDMI_VENDOR_IFRAME_LINE_NUMBER 3 @@ -351,6 +344,7 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, scrambler_on = true; tmds_clock_ratio = 1; } else { + tmds_clock_ratio = 0; scrambler_on = connector->supports_scramble; } @@ -396,6 +390,14 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, rc = _sde_hdmi_bridge_setup_ddc_timers(hdmi, HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, timeout_hsync); } else { + /* reset tmds clock ratio */ + rc = sde_hdmi_scdc_write(hdmi, + HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE, + tmds_clock_ratio); + /* scdc write can fail if sink doesn't support SCDC */ + if (rc && connector->scdc_present) + SDE_ERROR("SCDC present, TMDS clk ratio err\n"); + sde_hdmi_scdc_write(hdmi, HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0); reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); reg_val &= ~BIT(28); /* Unset SCRAMBLER_EN bit */ @@ -518,12 +520,17 @@ static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge) { struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct sde_connector *c_conn = to_sde_connector(hdmi->connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; /* need to update hdcp info here to ensure right HDCP support*/ sde_hdmi_update_hdcp_info(hdmi->connector); /* start HDCP authentication */ sde_hdmi_start_hdcp(hdmi->connector); + + /* reset HDR state */ + display->curr_hdr_state = HDR_DISABLE; } static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) @@ -568,18 +575,49 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) } static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi, - const struct drm_display_mode *mode) + struct drm_display_mode *mode) { u8 avi_iframe[HDMI_AVI_INFOFRAME_BUFFER_SIZE] = {0}; u8 *avi_frame = &avi_iframe[HDMI_INFOFRAME_HEADER_SIZE]; u8 checksum; u32 reg_val; + u32 mode_fmt_flags = 0; struct hdmi_avi_infoframe info; + struct drm_connector *connector; + + if (!hdmi || !mode) { + SDE_ERROR("invalid input\n"); + return; + } + connector = hdmi->connector; + + if (!connector) { + SDE_ERROR("invalid input\n"); + return; + } + + /* Cache the format flags before clearing */ + mode_fmt_flags = mode->flags; + /** + * Clear the RGB/YUV format flags before calling upstream API + * as the API also compares the flags and then returns a mode + */ + mode->flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK; drm_hdmi_avi_infoframe_from_display_mode(&info, mode); + /* Restore the format flags */ + mode->flags = mode_fmt_flags; - if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) + if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) { info.colorspace = HDMI_COLORSPACE_YUV420; + /** + * If sink supports quantization select, + * override to full range + */ + if (connector->yuv_qs) + info.ycc_quantization_range = + HDMI_YCC_QUANTIZATION_RANGE_FULL; + } hdmi_avi_infoframe_pack(&info, avi_iframe, sizeof(avi_iframe)); checksum = avi_iframe[HDMI_INFOFRAME_HEADER_SIZE - 1]; @@ -836,6 +874,11 @@ static bool _sde_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, return true; } +void sde_hdmi_bridge_power_on(struct drm_bridge *bridge) +{ + _sde_hdmi_bridge_power_on(bridge); +} + static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { .pre_enable = _sde_hdmi_bridge_pre_enable, .enable = _sde_hdmi_bridge_enable, diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c index ba47b7702efd..a291a1112aeb 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c @@ -68,6 +68,15 @@ static void sde_hdmi_hdcp2p2_ddc_clear_status(struct sde_hdmi *display) hdmi_write(hdmi, HDMI_HDCP2P2_DDC_STATUS, reg_val); } +static const char *sde_hdmi_hdr_sname(enum sde_hdmi_hdr_state hdr_state) +{ + switch (hdr_state) { + case HDR_DISABLE: return "HDR_DISABLE"; + case HDR_ENABLE: return "HDR_ENABLE"; + default: return "HDR_INVALID_STATE"; + } +} + /** * sde_hdmi_dump_regs - utility to dump HDMI regs * @hdmi_display: Pointer to private display handle @@ -898,3 +907,50 @@ int sde_hdmi_sink_dc_support(struct drm_connector *connector, return dc_format; } + +u8 sde_hdmi_hdr_get_ops(u8 curr_state, + u8 new_state) +{ + + /** There could be 3 valid state transitions: + * 1. HDR_DISABLE -> HDR_ENABLE + * + * In this transition, we shall start sending + * HDR metadata with metadata from the HDR clip + * + * 2. HDR_ENABLE -> HDR_ENABLE + * + * In this transition, we will keep sending + * HDR metadata but with EOTF and metadata as 0 + * + * 3. HDR_ENABLE -> HDR_DISABLE + * + * In this transition, we will stop sending + * metadata to the sink and clear PKT_CTRL register + * bits. + */ + + if ((curr_state == HDR_DISABLE) + && (new_state == HDR_ENABLE)) { + HDMI_UTIL_DEBUG("State changed %s ---> %s\n", + sde_hdmi_hdr_sname(curr_state), + sde_hdmi_hdr_sname(new_state)); + return HDR_SEND_INFO; + } else if ((curr_state == HDR_ENABLE) + && (new_state == HDR_ENABLE)) { + HDMI_UTIL_DEBUG("State changed %s ---> %s\n", + sde_hdmi_hdr_sname(curr_state), + sde_hdmi_hdr_sname(new_state)); + return HDR_SEND_INFO; + } else if ((curr_state == HDR_ENABLE) + && (new_state == HDR_DISABLE)) { + HDMI_UTIL_DEBUG("State changed %s ---> %s\n", + sde_hdmi_hdr_sname(curr_state), + sde_hdmi_hdr_sname(new_state)); + return HDR_CLEAR_INFO; + } + + HDMI_UTIL_DEBUG("Unsupported OR no state change\n"); + return HDR_UNSUPPORTED_OP; +} + diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h index 10effed54a14..3c6b0f1b9dd4 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h @@ -97,6 +97,14 @@ #define HDMI_GET_MSB(x)(x >> 8) #define HDMI_GET_LSB(x)(x & 0xff) +#define SDE_HDMI_VIC_640x480 0x1 +#define SDE_HDMI_YCC_QUANT_MASK (0x3 << 14) +#define SDE_HDMI_COLORIMETRY_MASK (0x3 << 22) + +#define SDE_HDMI_DEFAULT_COLORIMETRY 0x0 +#define SDE_HDMI_USE_EXTENDED_COLORIMETRY 0x3 +#define SDE_HDMI_BT2020_COLORIMETRY 0x6 + /* * Bits 1:0 in HDMI_HW_DDC_CTRL that dictate how the HDCP 2.2 RxStatus will be * read by the hardware @@ -125,6 +133,17 @@ enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask { RXSTATUS_REAUTH_REQ = BIT(14), }; +enum sde_hdmi_hdr_state { + HDR_DISABLE, + HDR_ENABLE +}; + +enum sde_hdmi_hdr_op { + HDR_UNSUPPORTED_OP, + HDR_SEND_INFO, + HDR_CLEAR_INFO +}; + struct sde_hdmi_tx_hdcp2p2_ddc_data { enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask intr_mask; u32 timeout_ms; @@ -170,4 +189,6 @@ bool sde_hdmi_validate_pixclk(struct drm_connector *connector, unsigned long pclk); int sde_hdmi_sink_dc_support(struct drm_connector *connector, struct drm_display_mode *mode); +u8 sde_hdmi_hdr_get_ops(u8 curr_state, + u8 new_state); #endif /* _SDE_HDMI_UTIL_H_ */ diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 924ce64206f4..969af4c6f0c0 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -293,7 +293,7 @@ static int msm_unload(struct drm_device *dev) priv->vram.paddr, &attrs); } - sde_evtlog_destroy(); + sde_dbg_destroy(); sde_power_client_destroy(&priv->phandle, priv->pclient); sde_power_resource_deinit(pdev, &priv->phandle); @@ -423,11 +423,17 @@ static int msm_component_bind_all(struct device *dev, } #endif +static int msm_power_enable_wrapper(void *handle, void *client, bool enable) +{ + return sde_power_resource_enable(handle, client, enable); +} + static int msm_load(struct drm_device *dev, unsigned long flags) { struct platform_device *pdev = dev->platformdev; struct msm_drm_private *priv; struct msm_kms *kms; + struct sde_dbg_power_ctrl dbg_power_ctrl = { NULL }; int ret, i; priv = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -477,9 +483,13 @@ static int msm_load(struct drm_device *dev, unsigned long flags) if (ret) goto fail; - ret = sde_evtlog_init(dev->primary->debugfs_root); + dbg_power_ctrl.handle = &priv->phandle; + dbg_power_ctrl.client = priv->pclient; + dbg_power_ctrl.enable_fn = msm_power_enable_wrapper; + ret = sde_dbg_init(dev->primary->debugfs_root, &pdev->dev, + &dbg_power_ctrl); if (ret) { - dev_err(dev->dev, "failed to init evtlog: %d\n", ret); + dev_err(dev->dev, "failed to init sde dbg: %d\n", ret); goto fail; } @@ -1923,8 +1933,75 @@ static struct drm_driver msm_driver = { #ifdef CONFIG_PM_SLEEP static int msm_pm_suspend(struct device *dev) { - struct drm_device *ddev = dev_get_drvdata(dev); + struct drm_device *ddev; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector *conn; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct msm_drm_private *priv; + int ret = 0; + + if (!dev) + return -EINVAL; + + ddev = dev_get_drvdata(dev); + if (!ddev || !ddev->dev_private) + return -EINVAL; + + priv = ddev->dev_private; + SDE_EVT32(0); + /* acquire modeset lock(s) */ + drm_modeset_lock_all(ddev); + ctx = ddev->mode_config.acquire_ctx; + + /* save current state for resume */ + if (priv->suspend_state) + drm_atomic_state_free(priv->suspend_state); + priv->suspend_state = drm_atomic_helper_duplicate_state(ddev, ctx); + if (IS_ERR_OR_NULL(priv->suspend_state)) { + DRM_ERROR("failed to back up suspend state\n"); + priv->suspend_state = NULL; + goto unlock; + } + + /* create atomic state to disable all CRTCs */ + state = drm_atomic_state_alloc(ddev); + if (IS_ERR_OR_NULL(state)) { + DRM_ERROR("failed to allocate crtc disable state\n"); + goto unlock; + } + + state->acquire_ctx = ctx; + drm_for_each_connector(conn, ddev) { + + if (!conn->state || !conn->state->crtc || + conn->dpms != DRM_MODE_DPMS_ON) + continue; + + /* force CRTC to be inactive */ + crtc_state = drm_atomic_get_crtc_state(state, + conn->state->crtc); + if (IS_ERR_OR_NULL(crtc_state)) { + DRM_ERROR("failed to get crtc %d state\n", + conn->state->crtc->base.id); + drm_atomic_state_free(state); + goto unlock; + } + crtc_state->active = false; + } + + /* commit the "disable all" state */ + ret = drm_atomic_commit(state); + if (ret < 0) { + DRM_ERROR("failed to disable crtcs, %d\n", ret); + drm_atomic_state_free(state); + } + +unlock: + drm_modeset_unlock_all(ddev); + + /* disable hot-plug polling */ drm_kms_helper_poll_disable(ddev); return 0; @@ -1932,8 +2009,38 @@ static int msm_pm_suspend(struct device *dev) static int msm_pm_resume(struct device *dev) { - struct drm_device *ddev = dev_get_drvdata(dev); + struct drm_device *ddev; + struct msm_drm_private *priv; + int ret; + + if (!dev) + return -EINVAL; + + ddev = dev_get_drvdata(dev); + if (!ddev || !ddev->dev_private) + return -EINVAL; + + priv = ddev->dev_private; + + SDE_EVT32(priv->suspend_state != NULL); + + drm_mode_config_reset(ddev); + + drm_modeset_lock_all(ddev); + + if (priv->suspend_state) { + priv->suspend_state->acquire_ctx = + ddev->mode_config.acquire_ctx; + ret = drm_atomic_commit(priv->suspend_state); + if (ret < 0) { + DRM_ERROR("failed to restore state, %d\n", ret); + drm_atomic_state_free(priv->suspend_state); + } + priv->suspend_state = NULL; + } + drm_modeset_unlock_all(ddev); + /* enable hot-plug polling */ drm_kms_helper_poll_enable(ddev); return 0; diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index ac15f399df7d..ae3a930005b6 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -144,7 +144,7 @@ enum msm_mdp_conn_property { /* blob properties, always put these first */ CONNECTOR_PROP_SDE_INFO, CONNECTOR_PROP_HDR_INFO, - CONNECTOR_PROP_HDR_METADATA, + CONNECTOR_PROP_HDR_CONTROL, /* # of blob properties */ CONNECTOR_PROP_BLOBCOUNT, @@ -162,6 +162,7 @@ enum msm_mdp_conn_property { /* enum/bitmask properties */ CONNECTOR_PROP_TOPOLOGY_NAME, CONNECTOR_PROP_TOPOLOGY_CONTROL, + CONNECTOR_PROP_LP, /* total # of properties */ CONNECTOR_PROP_COUNT @@ -237,10 +238,10 @@ struct msm_display_info { /** * struct - msm_display_kickoff_params - info for display features at kickoff - * @hdr_metadata: HDR metadata info passed from userspace + * @hdr_ctrl: HDR control info passed from userspace */ struct msm_display_kickoff_params { - struct drm_msm_ext_panel_hdr_metadata *hdr_metadata; + struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl; }; /** @@ -367,6 +368,9 @@ struct msm_drm_private { struct msm_vblank_ctrl vblank_ctrl; + /* saved atomic state during system suspend */ + struct drm_atomic_state *suspend_state; + /* list of clients waiting for events */ struct list_head client_event_list; }; @@ -414,6 +418,15 @@ void __msm_fence_worker(struct work_struct *work); (_cb)->func = _func; \ } while (0) +static inline bool msm_is_suspend_state(struct drm_device *dev) +{ + if (!dev || !dev->dev_private) + return false; + + return ((struct msm_drm_private *)dev->dev_private)->suspend_state != + NULL; +} + int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, bool async); diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index df9ddadc5c5c..0cd458fd184b 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -149,7 +149,6 @@ struct msm_gem_submit { uint32_t fence; int ring; u32 flags; - bool valid; uint64_t profile_buf_iova; struct drm_msm_gem_submit_profile_buffer *profile_buf; bool secure; diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index b73379aa9ed7..f2b6aa29b410 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -212,15 +212,8 @@ static int submit_validate_objects(struct msm_gpu *gpu, int contended, slow_locked = -1, i, ret = 0; retry: - submit->valid = true; - for (i = 0; i < submit->nr_bos; i++) { struct msm_gem_object *msm_obj = submit->bos[i].obj; - struct msm_gem_address_space *aspace; - uint64_t iova; - - aspace = (msm_obj->flags & MSM_BO_SECURE) ? - gpu->secure_aspace : submit->aspace; if (slow_locked == i) slow_locked = -1; @@ -247,28 +240,6 @@ retry: goto fail; } } - - /* if locking succeeded, pin bo: */ - ret = msm_gem_get_iova(&msm_obj->base, aspace, &iova); - - /* this would break the logic in the fail path.. there is no - * reason for this to happen, but just to be on the safe side - * let's notice if this starts happening in the future: - */ - WARN_ON(ret == -EDEADLK); - - if (ret) - goto fail; - - submit->bos[i].flags |= BO_PINNED; - - if (iova == submit->bos[i].iova) { - submit->bos[i].flags |= BO_VALID; - } else { - submit->bos[i].iova = iova; - submit->bos[i].flags &= ~BO_VALID; - submit->valid = false; - } } ww_acquire_done(&submit->ticket); @@ -297,9 +268,14 @@ fail: return ret; } -static int submit_bo(struct msm_gem_submit *submit, uint32_t idx, +static int submit_bo(struct msm_gpu *gpu, + struct msm_gem_submit *submit, uint32_t idx, struct msm_gem_object **obj, uint64_t *iova, bool *valid) { + struct msm_gem_object *msm_obj; + struct msm_gem_address_space *aspace; + int ret; + if (idx >= submit->nr_bos) { DRM_ERROR("invalid buffer index: %u (out of %u)\n", idx, submit->nr_bos); @@ -308,6 +284,39 @@ static int submit_bo(struct msm_gem_submit *submit, uint32_t idx, if (obj) *obj = submit->bos[idx].obj; + + /* Only map and pin if the caller needs either the iova or valid */ + if (!iova && !valid) + return 0; + + if (!(submit->bos[idx].flags & BO_PINNED)) { + uint64_t buf_iova; + + msm_obj = submit->bos[idx].obj; + aspace = (msm_obj->flags & MSM_BO_SECURE) ? + gpu->secure_aspace : submit->aspace; + + ret = msm_gem_get_iova(&msm_obj->base, aspace, &buf_iova); + + /* this would break the logic in the fail path.. there is no + * reason for this to happen, but just to be on the safe side + * let's notice if this starts happening in the future: + */ + WARN_ON(ret == -EDEADLK); + + if (ret) + return ret; + + submit->bos[idx].flags |= BO_PINNED; + + if (buf_iova == submit->bos[idx].iova) { + submit->bos[idx].flags |= BO_VALID; + } else { + submit->bos[idx].iova = buf_iova; + submit->bos[idx].flags &= ~BO_VALID; + } + } + if (iova) *iova = submit->bos[idx].iova; if (valid) @@ -317,8 +326,10 @@ static int submit_bo(struct msm_gem_submit *submit, uint32_t idx, } /* process the reloc's and patch up the cmdstream as needed: */ -static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj, - uint32_t offset, uint32_t nr_relocs, uint64_t relocs) +static int submit_reloc(struct msm_gpu *gpu, + struct msm_gem_submit *submit, + struct msm_gem_object *obj, uint32_t offset, + uint32_t nr_relocs, uint64_t relocs) { uint32_t i, last_offset = 0; uint32_t *ptr; @@ -334,6 +345,9 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob return -EINVAL; } + if (nr_relocs == 0) + return 0; + /* For now, just map the entire thing. Eventually we probably * to do it page-by-page, w/ kmap() if not vmap()d.. */ @@ -372,7 +386,8 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob return -EINVAL; } - ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid); + ret = submit_bo(gpu, submit, submit_reloc.reloc_idx, + NULL, &iova, &valid); if (ret) return ret; @@ -482,7 +497,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, goto out; } - ret = submit_bo(submit, submit_cmd.submit_idx, + ret = submit_bo(gpu, submit, submit_cmd.submit_idx, &msm_obj, &iova, NULL); if (ret) goto out; @@ -515,11 +530,9 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, + submit_cmd.submit_offset; } - if (submit->valid) - continue; - - ret = submit_reloc(submit, msm_obj, submit_cmd.submit_offset, - submit_cmd.nr_relocs, submit_cmd.relocs); + ret = submit_reloc(gpu, submit, msm_obj, + submit_cmd.submit_offset, submit_cmd.nr_relocs, + submit_cmd.relocs); if (ret) goto out; } diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 8073898e4275..7c109fdab545 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -863,7 +863,14 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, gpu->dev = drm; gpu->funcs = funcs; gpu->name = name; - gpu->inactive = true; + /* + * Set the inactive flag to false, so that when the retire worker + * kicks in from the init path, it knows that it has to turn off the + * clocks. This should be fine to do since this is the init sequence + * and we have an init_lock in msm_open() to protect against bad things + * from happening. + */ + gpu->inactive = false; INIT_LIST_HEAD(&gpu->active_list); INIT_WORK(&gpu->retire_work, retire_worker); @@ -897,6 +904,7 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, ret = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, IRQF_TRIGGER_HIGH, gpu->name, gpu); if (ret) { + gpu->irq = ret; dev_err(drm->dev, "failed to request IRQ%u: %d\n", gpu->irq, ret); goto fail; } @@ -1007,6 +1015,11 @@ void msm_gpu_cleanup(struct msm_gpu *gpu) WARN_ON(!list_empty(&gpu->active_list)); + if (gpu->irq >= 0) { + disable_irq(gpu->irq); + devm_free_irq(&pdev->dev, gpu->irq, gpu); + } + bs_fini(gpu); for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) @@ -1022,4 +1035,22 @@ void msm_gpu_cleanup(struct msm_gpu *gpu) msm_gpu_destroy_address_space(gpu->aspace); msm_gpu_destroy_address_space(gpu->secure_aspace); + + if (gpu->gpu_reg) + devm_regulator_put(gpu->gpu_reg); + + if (gpu->gpu_cx) + devm_regulator_put(gpu->gpu_cx); + + if (gpu->ebi1_clk) + devm_clk_put(&pdev->dev, gpu->ebi1_clk); + + for (i = gpu->nr_clocks - 1; i >= 0; i--) + if (gpu->grp_clks[i]) + devm_clk_put(&pdev->dev, gpu->grp_clks[i]); + + devm_kfree(&pdev->dev, gpu->grp_clks); + + if (gpu->mmio) + devm_iounmap(&pdev->dev, gpu->mmio); } diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index 8148d3e9e850..cd3a710f8f27 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -46,6 +46,8 @@ struct msm_mmu_funcs { void (*destroy)(struct msm_mmu *mmu); void (*enable)(struct msm_mmu *mmu); void (*disable)(struct msm_mmu *mmu); + int (*set_property)(struct msm_mmu *mmu, + enum iommu_attr attr, void *data); }; struct msm_mmu { diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c index eb68eb977aa7..4247243055b6 100644 --- a/drivers/gpu/drm/msm/msm_smmu.c +++ b/drivers/gpu/drm/msm/msm_smmu.c @@ -170,12 +170,36 @@ static void msm_smmu_destroy(struct msm_mmu *mmu) kfree(smmu); } +/* user can call this API to set the attribute of smmu*/ +static int msm_smmu_set_property(struct msm_mmu *mmu, + enum iommu_attr attr, void *data) +{ + struct msm_smmu *smmu = to_msm_smmu(mmu); + struct msm_smmu_client *client = msm_smmu_to_client(smmu); + struct iommu_domain *domain; + int ret = 0; + + if (!client) + return -EINVAL; + + domain = client->mmu_mapping->domain; + if (!domain) + return -EINVAL; + + ret = iommu_domain_set_attr(domain, attr, data); + if (ret) + DRM_ERROR("set domain attribute failed\n"); + + return ret; +} + static const struct msm_mmu_funcs funcs = { .attach = msm_smmu_attach, .detach = msm_smmu_detach, .map = msm_smmu_map, .unmap = msm_smmu_unmap, .destroy = msm_smmu_destroy, + .set_property = msm_smmu_set_property, }; static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = { diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c index ef7492817983..a0f6b5c6a732 100644 --- a/drivers/gpu/drm/msm/sde/sde_color_processing.c +++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c @@ -344,8 +344,8 @@ static void sde_cp_crtc_install_immutable_property(struct drm_crtc *crtc, prop = priv->cp_property[feature]; if (!prop) { - prop = drm_property_create(crtc->dev, DRM_MODE_PROP_IMMUTABLE, - name, 0); + prop = drm_property_create_range(crtc->dev, + DRM_MODE_PROP_IMMUTABLE, name, 0, 1); if (!prop) { DRM_ERROR("property create failed: %s\n", name); kfree(prop_node); diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 76e6e4ef6e7d..a1cbd0cdf795 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -10,7 +10,7 @@ * GNU General Public License for more details. */ -#define pr_fmt(fmt) "sde-drm:[%s] " fmt, __func__ +#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ #include "msm_drv.h" #include "sde_kms.h" @@ -38,6 +38,13 @@ static const struct drm_prop_enum_list e_topology_control[] = { {SDE_RM_TOPCTL_PPSPLIT, "ppsplit"} }; +static const struct drm_prop_enum_list e_power_mode[] = { + {SDE_MODE_DPMS_ON, "ON"}, + {SDE_MODE_DPMS_LP1, "LP1"}, + {SDE_MODE_DPMS_LP2, "LP2"}, + {SDE_MODE_DPMS_OFF, "OFF"}, +}; + int sde_connector_get_info(struct drm_connector *connector, struct msm_display_info *info) { @@ -83,13 +90,57 @@ int sde_connector_pre_kickoff(struct drm_connector *connector) if (!c_conn->ops.pre_kickoff) return 0; - params.hdr_metadata = &c_state->hdr_meta; + params.hdr_ctrl = &c_state->hdr_ctrl; rc = c_conn->ops.pre_kickoff(connector, c_conn->display, ¶ms); return rc; } +enum sde_csc_type sde_connector_get_csc_type(struct drm_connector *conn) +{ + struct sde_connector *c_conn; + + if (!conn) { + SDE_ERROR("invalid argument\n"); + return -EINVAL; + } + + c_conn = to_sde_connector(conn); + + if (!c_conn->display) { + SDE_ERROR("invalid argument\n"); + return -EINVAL; + } + + if (!c_conn->ops.get_csc_type) + return SDE_CSC_RGB2YUV_601L; + + return c_conn->ops.get_csc_type(conn, c_conn->display); +} + +bool sde_connector_mode_needs_full_range(struct drm_connector *connector) +{ + struct sde_connector *c_conn; + + if (!connector) { + SDE_ERROR("invalid argument\n"); + return false; + } + + c_conn = to_sde_connector(connector); + + if (!c_conn->display) { + SDE_ERROR("invalid argument\n"); + return false; + } + + if (!c_conn->ops.mode_needs_full_range) + return false; + + return c_conn->ops.mode_needs_full_range(c_conn->display); +} + static void sde_connector_destroy(struct drm_connector *connector) { struct sde_connector *c_conn; @@ -111,6 +162,7 @@ static void sde_connector_destroy(struct drm_connector *connector) msm_property_destroy(&c_conn->property_info); drm_connector_unregister(connector); + mutex_destroy(&c_conn->lock); sde_fence_deinit(&c_conn->retire_fence); drm_connector_cleanup(connector); kfree(c_conn); @@ -247,6 +299,7 @@ static int _sde_connector_set_hdr_info( void *usr_ptr) { struct drm_connector *connector; + struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl; struct drm_msm_ext_panel_hdr_metadata *hdr_meta; int i; @@ -262,21 +315,26 @@ static int _sde_connector_set_hdr_info( return -ENOTSUPP; } - memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta)); + memset(&c_state->hdr_ctrl, 0, sizeof(c_state->hdr_ctrl)); if (!usr_ptr) { - SDE_DEBUG_CONN(c_conn, "hdr metadata cleared\n"); + SDE_DEBUG_CONN(c_conn, "hdr control cleared\n"); return 0; } - if (copy_from_user(&c_state->hdr_meta, + if (copy_from_user(&c_state->hdr_ctrl, (void __user *)usr_ptr, - sizeof(*hdr_meta))) { - SDE_ERROR_CONN(c_conn, "failed to copy hdr metadata\n"); + sizeof(*hdr_ctrl))) { + SDE_ERROR_CONN(c_conn, "failed to copy hdr control\n"); return -EFAULT; } - hdr_meta = &c_state->hdr_meta; + hdr_ctrl = &c_state->hdr_ctrl; + + SDE_DEBUG_CONN(c_conn, "hdr_supported %d\n", + hdr_ctrl->hdr_state); + + hdr_meta = &hdr_ctrl->hdr_meta; SDE_DEBUG_CONN(c_conn, "hdr_supported %d\n", hdr_meta->hdr_supported); @@ -303,6 +361,56 @@ static int _sde_connector_set_hdr_info( return 0; } +static int _sde_connector_update_power_locked(struct sde_connector *c_conn) +{ + struct drm_connector *connector; + void *display; + int (*set_power)(struct drm_connector *, int, void *); + int mode, rc = 0; + + if (!c_conn) + return -EINVAL; + connector = &c_conn->base; + + mode = c_conn->lp_mode; + if (c_conn->dpms_mode != DRM_MODE_DPMS_ON) + mode = SDE_MODE_DPMS_OFF; + switch (c_conn->dpms_mode) { + case DRM_MODE_DPMS_ON: + mode = c_conn->lp_mode; + break; + case DRM_MODE_DPMS_STANDBY: + mode = SDE_MODE_DPMS_STANDBY; + break; + case DRM_MODE_DPMS_SUSPEND: + mode = SDE_MODE_DPMS_SUSPEND; + break; + case DRM_MODE_DPMS_OFF: + mode = SDE_MODE_DPMS_OFF; + break; + default: + mode = c_conn->lp_mode; + SDE_ERROR("conn %d dpms set to unrecognized mode %d\n", + connector->base.id, mode); + break; + } + + SDE_DEBUG("conn %d - dpms %d, lp %d, panel %d\n", connector->base.id, + c_conn->dpms_mode, c_conn->lp_mode, mode); + + if (mode != c_conn->last_panel_power_mode && c_conn->ops.set_power) { + display = c_conn->display; + set_power = c_conn->ops.set_power; + + mutex_unlock(&c_conn->lock); + rc = set_power(connector, mode, display); + mutex_lock(&c_conn->lock); + } + c_conn->last_panel_power_mode = mode; + + return rc; +} + static int sde_connector_atomic_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, @@ -329,8 +437,8 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, /* connector-specific property handling */ idx = msm_property_index(&c_conn->property_info, property); - - if (idx == CONNECTOR_PROP_OUT_FB) { + switch (idx) { + case CONNECTOR_PROP_OUT_FB: /* clear old fb, if present */ if (c_state->out_fb) _sde_connector_destroy_fb(c_conn, c_state); @@ -354,15 +462,23 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, if (rc) SDE_ERROR("prep fb failed, %d\n", rc); } - } - - if (idx == CONNECTOR_PROP_TOPOLOGY_CONTROL) { + break; + case CONNECTOR_PROP_TOPOLOGY_CONTROL: rc = sde_rm_check_property_topctl(val); if (rc) SDE_ERROR("invalid topology_control: 0x%llX\n", val); - } - - if (idx == CONNECTOR_PROP_HDR_METADATA) { + break; + case CONNECTOR_PROP_LP: + mutex_lock(&c_conn->lock); + c_conn->lp_mode = val; + _sde_connector_update_power_locked(c_conn); + mutex_unlock(&c_conn->lock); + break; + default: + break; + } + + if (idx == CONNECTOR_PROP_HDR_CONTROL) { rc = _sde_connector_set_hdr_info(c_conn, c_state, (void *)val); if (rc) SDE_ERROR_CONN(c_conn, "cannot set hdr info %d\n", rc); @@ -460,6 +576,60 @@ void sde_connector_complete_commit(struct drm_connector *connector) sde_fence_signal(&to_sde_connector(connector)->retire_fence, 0); } +static int sde_connector_dpms(struct drm_connector *connector, + int mode) +{ + struct sde_connector *c_conn; + + if (!connector) { + SDE_ERROR("invalid connector\n"); + return -EINVAL; + } + c_conn = to_sde_connector(connector); + + /* validate incoming dpms request */ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + SDE_DEBUG("conn %d dpms set to %d\n", + connector->base.id, mode); + break; + default: + SDE_ERROR("conn %d dpms set to unrecognized mode %d\n", + connector->base.id, mode); + break; + } + + mutex_lock(&c_conn->lock); + c_conn->dpms_mode = mode; + _sde_connector_update_power_locked(c_conn); + mutex_unlock(&c_conn->lock); + + /* use helper for boilerplate handling */ + return drm_atomic_helper_connector_dpms(connector, mode); +} + +int sde_connector_get_dpms(struct drm_connector *connector) +{ + struct sde_connector *c_conn; + int rc; + + if (!connector) { + SDE_DEBUG("invalid connector\n"); + return DRM_MODE_DPMS_OFF; + } + + c_conn = to_sde_connector(connector); + + mutex_lock(&c_conn->lock); + rc = c_conn->dpms_mode; + mutex_unlock(&c_conn->lock); + + return rc; +} + static void sde_connector_update_hdr_props(struct drm_connector *connector) { struct sde_connector *c_conn = to_sde_connector(connector); @@ -508,7 +678,7 @@ sde_connector_detect(struct drm_connector *connector, bool force) } static const struct drm_connector_funcs sde_connector_ops = { - .dpms = drm_atomic_helper_connector_dpms, + .dpms = sde_connector_dpms, .reset = sde_connector_atomic_reset, .detect = sde_connector_detect, .destroy = sde_connector_destroy, @@ -598,6 +768,7 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, struct sde_kms *sde_kms; struct sde_kms_info *info; struct sde_connector *c_conn = NULL; + struct sde_splash_info *sinfo; int rc; if (!dev || !dev->dev_private || !encoder) { @@ -630,6 +801,11 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, c_conn->panel = panel; c_conn->display = display; + c_conn->dpms_mode = DRM_MODE_DPMS_ON; + c_conn->lp_mode = 0; + c_conn->last_panel_power_mode = SDE_MODE_DPMS_ON; + + sde_kms = to_sde_kms(priv->kms); if (sde_kms->vbif[VBIF_NRT]) { c_conn->aspace[SDE_IOMMU_DOMAIN_UNSECURE] = @@ -663,6 +839,8 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, goto error_cleanup_conn; } + mutex_init(&c_conn->lock); + rc = drm_connector_register(&c_conn->base); if (rc) { SDE_ERROR("failed to register drm connector, %d\n", rc); @@ -718,8 +896,8 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, } msm_property_install_volatile_range(&c_conn->property_info, - "hdr_metadata", 0x0, 0, ~0, 0, - CONNECTOR_PROP_HDR_METADATA); + "hdr_control", 0x0, 0, ~0, 0, + CONNECTOR_PROP_HDR_CONTROL); msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE", 0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE); @@ -742,6 +920,11 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, ARRAY_SIZE(e_topology_control), CONNECTOR_PROP_TOPOLOGY_CONTROL, 0); + msm_property_install_enum(&c_conn->property_info, "LP", + 0, 0, e_power_mode, + ARRAY_SIZE(e_power_mode), + CONNECTOR_PROP_LP, 0); + rc = msm_property_install_get_status(&c_conn->property_info); if (rc) { SDE_ERROR("failed to create one or more properties\n"); @@ -751,6 +934,10 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, SDE_DEBUG("connector %d attach encoder %d\n", c_conn->base.base.id, encoder->base.id); + sinfo = &sde_kms->splash_info; + if (sinfo && sinfo->handoff) + sde_splash_setup_connector_count(sinfo, connector_type); + priv->connectors[priv->num_connectors++] = &c_conn->base; return &c_conn->base; @@ -764,6 +951,7 @@ error_destroy_property: error_unregister_conn: drm_connector_unregister(&c_conn->base); error_cleanup_fence: + mutex_destroy(&c_conn->lock); sde_fence_deinit(&c_conn->retire_fence); error_cleanup_conn: drm_connector_cleanup(&c_conn->base); diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h index 19e2b8a3e41c..f9b8c3966d74 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -135,6 +135,38 @@ struct sde_connector_ops { int (*pre_kickoff)(struct drm_connector *connector, void *display, struct msm_display_kickoff_params *params); + + /** + * mode_needs_full_range - does the mode need full range + * quantization + * @display: Pointer to private display structure + * Returns: true or false based on whether full range is needed + */ + bool (*mode_needs_full_range)(void *display); + + /** + * get_csc_type - returns the CSC type to be used + * by the CDM block based on HDR state + * @connector: Pointer to drm connector structure + * @display: Pointer to private display structure + * Returns: type of CSC matrix to be used + */ + enum sde_csc_type (*get_csc_type)(struct drm_connector *connector, + void *display); + + /** + * set_power - update dpms setting + * @connector: Pointer to drm connector structure + * @power_mode: One of the following, + * SDE_MODE_DPMS_ON + * SDE_MODE_DPMS_LP1 + * SDE_MODE_DPMS_LP2 + * SDE_MODE_DPMS_OFF + * @display: Pointer to private display structure + * Returns: Zero on success + */ + int (*set_power)(struct drm_connector *connector, + int power_mode, void *display); }; /** @@ -147,8 +179,12 @@ struct sde_connector_ops { * @mmu_secure: MMU id for secure buffers * @mmu_unsecure: MMU id for unsecure buffers * @name: ASCII name of connector + * @lock: Mutex lock object for this structure * @retire_fence: Retire fence reference * @ops: Local callback function pointer table + * @dpms_mode: DPMS property setting from user space + * @lp_mode: LP property setting from user space + * @last_panel_power_mode: Last consolidated dpms/lp mode setting * @property_info: Private structure for generic property handling * @property_data: Array of private data for generic property handling * @blob_caps: Pointer to blob structure for 'capabilities' property @@ -167,8 +203,12 @@ struct sde_connector { char name[SDE_CONNECTOR_NAME_SIZE]; + struct mutex lock; struct sde_fence retire_fence; struct sde_connector_ops ops; + int dpms_mode; + int lp_mode; + int last_panel_power_mode; struct msm_property_info property_info; struct msm_property_data property_data[CONNECTOR_PROP_COUNT]; @@ -221,14 +261,14 @@ struct sde_connector { * @out_fb: Pointer to output frame buffer, if applicable * @aspace: Address space for accessing frame buffer objects, if applicable * @property_values: Local cache of current connector property values - * @hdr_meta: HDR metadata info passed from userspace + * @hdr_ctrl: HDR control info passed from userspace */ struct sde_connector_state { struct drm_connector_state base; struct drm_framebuffer *out_fb; struct msm_gem_address_space *aspace; uint64_t property_values[CONNECTOR_PROP_COUNT]; - struct drm_msm_ext_panel_hdr_metadata hdr_meta; + struct drm_msm_ext_panel_hdr_ctrl hdr_ctrl; }; /** @@ -327,5 +367,28 @@ int sde_connector_get_info(struct drm_connector *connector, */ int sde_connector_pre_kickoff(struct drm_connector *connector); +/** + * sde_connector_mode_needs_full_range - query quantization type + * for the connector mode + * @connector: Pointer to drm connector object + * Returns: true OR false based on connector mode + */ +bool sde_connector_mode_needs_full_range(struct drm_connector *connector); + +/** + * sde_connector_get_csc_type - query csc type + * to be used for the connector + * @connector: Pointer to drm connector object + * Returns: csc type based on connector HDR state + */ +enum sde_csc_type sde_connector_get_csc_type(struct drm_connector *conn); + +/** + * sde_connector_get_dpms - query dpms setting + * @connector: Pointer to drm connector structure + * Returns: Current DPMS setting for connector + */ +int sde_connector_get_dpms(struct drm_connector *connector); + #endif /* _SDE_CONNECTOR_H_ */ diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c index dbfc2dd11a17..83c8982b2e00 100644 --- a/drivers/gpu/drm/msm/sde/sde_core_irq.c +++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c @@ -32,7 +32,7 @@ static void sde_core_irq_callback_handler(void *arg, int irq_idx) struct sde_irq_callback *cb; unsigned long irq_flags; - SDE_DEBUG("irq_idx=%d\n", irq_idx); + pr_debug("irq_idx=%d\n", irq_idx); if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx); diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index 5323c0194594..5d9a4e907318 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -57,7 +57,17 @@ static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc) { - struct msm_drm_private *priv = crtc->dev->dev_private; + struct msm_drm_private *priv; + + if (!crtc || !crtc->dev || !crtc->dev->dev_private) { + SDE_ERROR("invalid crtc\n"); + return NULL; + } + priv = crtc->dev->dev_private; + if (!priv || !priv->kms) { + SDE_ERROR("invalid kms\n"); + return NULL; + } return to_sde_kms(priv->kms); } @@ -77,10 +87,10 @@ static void sde_crtc_destroy(struct drm_crtc *crtc) sde_cp_crtc_destroy_properties(crtc); debugfs_remove_recursive(sde_crtc->debugfs_root); - mutex_destroy(&sde_crtc->crtc_lock); sde_fence_deinit(&sde_crtc->output_fence); drm_crtc_cleanup(crtc); + mutex_destroy(&sde_crtc->crtc_lock); kfree(sde_crtc); } @@ -590,14 +600,23 @@ void sde_crtc_complete_commit(struct drm_crtc *crtc, { struct sde_crtc *sde_crtc; struct sde_crtc_state *cstate; + struct drm_connector *conn; + struct sde_connector *c_conn; + struct drm_device *dev; + struct msm_drm_private *priv; + struct sde_kms *sde_kms; int i; - if (!crtc || !crtc->state) { + if (!crtc || !crtc->state || !crtc->dev) { SDE_ERROR("invalid crtc\n"); return; } + dev = crtc->dev; + priv = dev->dev_private; + sde_crtc = to_sde_crtc(crtc); + sde_kms = _sde_crtc_get_kms(crtc); cstate = to_sde_crtc_state(crtc->state); SDE_EVT32(DRMID(crtc)); @@ -606,6 +625,22 @@ void sde_crtc_complete_commit(struct drm_crtc *crtc, for (i = 0; i < cstate->num_connectors; ++i) sde_connector_complete_commit(cstate->connectors[i]); + + if (sde_splash_get_lk_complete_status(&sde_kms->splash_info)) { + mutex_lock(&dev->mode_config.mutex); + drm_for_each_connector(conn, crtc->dev) { + if (conn->state->crtc != crtc) + continue; + + c_conn = to_sde_connector(conn); + + sde_splash_clean_up_free_resource(priv->kms, + &priv->phandle, + c_conn->connector_type, + c_conn->display); + } + mutex_unlock(&dev->mode_config.mutex); + } } /** @@ -900,6 +935,15 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc) sde_kms = _sde_crtc_get_kms(crtc); priv = sde_kms->dev->dev_private; + /* + * If no mixers has been allocated in sde_crtc_atomic_check(), + * it means we are trying to start a CRTC whose state is disabled: + * nothing else needs to be done. + */ + if (unlikely(!sde_crtc->num_mixers)) + return; + + SDE_ATRACE_BEGIN("crtc_commit"); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->crtc != crtc) @@ -941,6 +985,120 @@ end: } /** + * _sde_crtc_vblank_enable_nolock - update power resource and vblank request + * @sde_crtc: Pointer to sde crtc structure + * @enable: Whether to enable/disable vblanks + * + * @Return: error code + */ +static int _sde_crtc_vblank_enable_no_lock( + struct sde_crtc *sde_crtc, bool enable) +{ + struct drm_device *dev; + struct drm_crtc *crtc; + struct drm_encoder *enc; + struct msm_drm_private *priv; + struct sde_kms *sde_kms; + int ret = 0; + + if (!sde_crtc) { + SDE_ERROR("invalid crtc\n"); + return -EINVAL; + } + + crtc = &sde_crtc->base; + dev = crtc->dev; + priv = dev->dev_private; + + if (!priv->kms) { + SDE_ERROR("invalid kms\n"); + return -EINVAL; + } + sde_kms = to_sde_kms(priv->kms); + + if (enable) { + ret = sde_power_resource_enable(&priv->phandle, + sde_kms->core_client, true); + if (ret) + return ret; + + list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { + if (enc->crtc != crtc) + continue; + + SDE_EVT32(DRMID(crtc), DRMID(enc), enable); + + sde_encoder_register_vblank_callback(enc, + sde_crtc_vblank_cb, (void *)crtc); + } + } else { + list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { + if (enc->crtc != crtc) + continue; + + SDE_EVT32(DRMID(crtc), DRMID(enc), enable); + + sde_encoder_register_vblank_callback(enc, NULL, NULL); + } + ret = sde_power_resource_enable(&priv->phandle, + sde_kms->core_client, false); + } + + return ret; +} + +/** + * _sde_crtc_set_suspend - notify crtc of suspend enable/disable + * @crtc: Pointer to drm crtc object + * @enable: true to enable suspend, false to indicate resume + */ +static void _sde_crtc_set_suspend(struct drm_crtc *crtc, bool enable) +{ + struct sde_crtc *sde_crtc; + struct msm_drm_private *priv; + struct sde_kms *sde_kms; + + if (!crtc || !crtc->dev || !crtc->dev->dev_private) { + SDE_ERROR("invalid crtc\n"); + return; + } + sde_crtc = to_sde_crtc(crtc); + priv = crtc->dev->dev_private; + + if (!priv->kms) { + SDE_ERROR("invalid crtc kms\n"); + return; + } + sde_kms = to_sde_kms(priv->kms); + + SDE_DEBUG("crtc%d suspend = %d\n", crtc->base.id, enable); + + mutex_lock(&sde_crtc->crtc_lock); + + /* + * Update CP on suspend/resume transitions + */ + if (enable && !sde_crtc->suspend) + sde_cp_crtc_suspend(crtc); + else if (!enable && sde_crtc->suspend) + sde_cp_crtc_resume(crtc); + + /* + * If the vblank refcount != 0, release a power reference on suspend + * and take it back during resume (if it is still != 0). + */ + if (sde_crtc->suspend == enable) + SDE_DEBUG("crtc%d suspend already set to %d, ignoring update\n", + crtc->base.id, enable); + else if (sde_crtc->enabled && sde_crtc->vblank_requested) + _sde_crtc_vblank_enable_no_lock(sde_crtc, !enable); + + sde_crtc->suspend = enable; + + mutex_unlock(&sde_crtc->crtc_lock); +} + +/** * sde_crtc_duplicate_state - state duplicate hook * @crtc: Pointer to drm crtc structure * @Returns: Pointer to new drm_crtc_state structure @@ -990,6 +1148,10 @@ static void sde_crtc_reset(struct drm_crtc *crtc) return; } + /* revert suspend actions, if necessary */ + if (msm_is_suspend_state(crtc->dev)) + _sde_crtc_set_suspend(crtc, false); + /* remove previous state, if present */ if (crtc->state) { sde_crtc_destroy_state(crtc, crtc->state); @@ -1015,37 +1177,43 @@ static void sde_crtc_reset(struct drm_crtc *crtc) static void sde_crtc_disable(struct drm_crtc *crtc) { - struct msm_drm_private *priv; - struct sde_crtc *sde_crtc; struct drm_encoder *encoder; + struct sde_crtc *sde_crtc; struct sde_kms *sde_kms; + struct msm_drm_private *priv; + int ret = 0; - if (!crtc) { + if (!crtc || !crtc->dev || !crtc->state) { SDE_ERROR("invalid crtc\n"); return; } sde_crtc = to_sde_crtc(crtc); sde_kms = _sde_crtc_get_kms(crtc); + if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev_private) { + SDE_ERROR("invalid kms handle\n"); + return; + } priv = sde_kms->dev->dev_private; SDE_DEBUG("crtc%d\n", crtc->base.id); + if (msm_is_suspend_state(crtc->dev)) + _sde_crtc_set_suspend(crtc, true); + mutex_lock(&sde_crtc->crtc_lock); - SDE_EVT32(DRMID(crtc)); + SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend, + sde_crtc->vblank_requested); - if (atomic_read(&sde_crtc->vblank_refcount)) { - SDE_ERROR("crtc%d invalid vblank refcount\n", - crtc->base.id); - SDE_EVT32(DRMID(crtc)); - drm_for_each_encoder(encoder, crtc->dev) { - if (encoder->crtc != crtc) - continue; - sde_encoder_register_vblank_callback(encoder, NULL, - NULL); - } - atomic_set(&sde_crtc->vblank_refcount, 0); + if (sde_crtc->enabled && !sde_crtc->suspend && + sde_crtc->vblank_requested) { + ret = _sde_crtc_vblank_enable_no_lock(sde_crtc, false); + if (ret) + SDE_ERROR("%s vblank enable failed: %d\n", + sde_crtc->name, ret); } + sde_crtc->enabled = false; + if (atomic_read(&sde_crtc->frame_pending)) { /* release bandwidth and other resources */ SDE_ERROR("crtc%d invalid frame pending\n", @@ -1080,6 +1248,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc) struct sde_hw_mixer_cfg cfg; struct drm_encoder *encoder; int i; + int ret = 0; if (!crtc) { SDE_ERROR("invalid crtc\n"); @@ -1108,6 +1277,19 @@ static void sde_crtc_enable(struct drm_crtc *crtc) sde_crtc_request_flip_cb, (void *)crtc); } + mutex_lock(&sde_crtc->crtc_lock); + SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend, + sde_crtc->vblank_requested); + if (!sde_crtc->enabled && !sde_crtc->suspend && + sde_crtc->vblank_requested) { + ret = _sde_crtc_vblank_enable_no_lock(sde_crtc, true); + if (ret) + SDE_ERROR("%s vblank enable failed: %d\n", + sde_crtc->name, ret); + } + sde_crtc->enabled = true; + mutex_unlock(&sde_crtc->crtc_lock); + for (i = 0; i < sde_crtc->num_mixers; i++) { lm = mixer[i].hw_lm; cfg.out_width = sde_crtc_mixer_width(sde_crtc, mode); @@ -1154,8 +1336,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, struct drm_display_mode *mode; int cnt = 0, rc = 0, mixer_width, i, z_pos; - int left_crtc_zpos_cnt[SDE_STAGE_MAX] = {0}; - int right_crtc_zpos_cnt[SDE_STAGE_MAX] = {0}; + int left_zpos_cnt = 0, right_zpos_cnt = 0; if (!crtc) { SDE_ERROR("invalid crtc\n"); @@ -1172,6 +1353,10 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, mode = &state->adjusted_mode; SDE_DEBUG("%s: check", sde_crtc->name); + /* force a full mode set if active state changed */ + if (state->active_changed) + state->mode_changed = true; + mixer_width = sde_crtc_mixer_width(sde_crtc, mode); /* get plane state for all drm planes associated with crtc state */ @@ -1205,11 +1390,12 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, } } + /* assign mixer stages based on sorted zpos property */ + sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL); + if (!sde_is_custom_client()) { int stage_old = pstates[0].stage; - /* assign mixer stages based on sorted zpos property */ - sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL); z_pos = 0; for (i = 0; i < cnt; i++) { if (stage_old != pstates[i].stage) @@ -1219,8 +1405,14 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, } } + z_pos = -1; for (i = 0; i < cnt; i++) { - z_pos = pstates[i].stage; + /* reset counts at every new blend stage */ + if (pstates[i].stage != z_pos) { + left_zpos_cnt = 0; + right_zpos_cnt = 0; + z_pos = pstates[i].stage; + } /* verify z_pos setting before using it */ if (z_pos >= SDE_STAGE_MAX - SDE_STAGE_0) { @@ -1229,22 +1421,24 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, rc = -EINVAL; goto end; } else if (pstates[i].drm_pstate->crtc_x < mixer_width) { - if (left_crtc_zpos_cnt[z_pos] == 2) { - SDE_ERROR("> 2 plane @ stage%d on left\n", + if (left_zpos_cnt == 2) { + SDE_ERROR("> 2 planes @ stage %d on left\n", z_pos); rc = -EINVAL; goto end; } - left_crtc_zpos_cnt[z_pos]++; + left_zpos_cnt++; + } else { - if (right_crtc_zpos_cnt[z_pos] == 2) { - SDE_ERROR("> 2 plane @ stage%d on right\n", + if (right_zpos_cnt == 2) { + SDE_ERROR("> 2 planes @ stage %d on right\n", z_pos); rc = -EINVAL; goto end; } - right_crtc_zpos_cnt[z_pos]++; + right_zpos_cnt++; } + pstates[i].sde_pstate->stage = z_pos + SDE_STAGE_0; SDE_DEBUG("%s: zpos %d", sde_crtc->name, z_pos); } @@ -1256,45 +1450,77 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, goto end; } + /* + * enforce pipe priority restrictions + * use pstates sorted by stage to check planes on same stage + * we assume that all pipes are in source split so its valid to compare + * without taking into account left/right mixer placement + */ + for (i = 1; i < cnt; i++) { + struct plane_state *prv_pstate, *cur_pstate; + int32_t prv_x, cur_x, prv_id, cur_id; + + prv_pstate = &pstates[i - 1]; + cur_pstate = &pstates[i]; + if (prv_pstate->stage != cur_pstate->stage) + continue; + + prv_x = prv_pstate->drm_pstate->crtc_x; + cur_x = cur_pstate->drm_pstate->crtc_x; + prv_id = prv_pstate->sde_pstate->base.plane->base.id; + cur_id = cur_pstate->sde_pstate->base.plane->base.id; + + /* + * Planes are enumerated in pipe-priority order such that planes + * with lower drm_id must be left-most in a shared blend-stage + * when using source split. + */ + if (cur_x > prv_x && cur_id < prv_id) { + SDE_ERROR( + "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n", + cur_pstate->stage, cur_id, cur_x, + prv_id, prv_x); + rc = -EINVAL; + goto end; + } else if (cur_x < prv_x && cur_id > prv_id) { + SDE_ERROR( + "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n", + cur_pstate->stage, prv_id, prv_x, + cur_id, cur_x); + rc = -EINVAL; + goto end; + } + } + + end: return rc; } int sde_crtc_vblank(struct drm_crtc *crtc, bool en) { - struct sde_crtc *sde_crtc = to_sde_crtc(crtc); - struct drm_encoder *encoder; - struct drm_device *dev = crtc->dev; + struct sde_crtc *sde_crtc; + int ret; - if (en && atomic_inc_return(&sde_crtc->vblank_refcount) == 1) { - SDE_DEBUG("crtc%d vblank enable\n", crtc->base.id); - } else if (!en && atomic_read(&sde_crtc->vblank_refcount) < 1) { - SDE_ERROR("crtc%d invalid vblank disable\n", crtc->base.id); + if (!crtc) { + SDE_ERROR("invalid crtc\n"); return -EINVAL; - } else if (!en && atomic_dec_return(&sde_crtc->vblank_refcount) == 0) { - SDE_DEBUG("crtc%d vblank disable\n", crtc->base.id); - } else { - SDE_DEBUG("crtc%d vblank %s refcount:%d\n", - crtc->base.id, - en ? "enable" : "disable", - atomic_read(&sde_crtc->vblank_refcount)); - return 0; } + sde_crtc = to_sde_crtc(crtc); - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc != crtc) - continue; - - SDE_EVT32(DRMID(crtc), en); - - if (en) - sde_encoder_register_vblank_callback(encoder, - sde_crtc_vblank_cb, (void *)crtc); - else - sde_encoder_register_vblank_callback(encoder, NULL, - NULL); + mutex_lock(&sde_crtc->crtc_lock); + SDE_EVT32(DRMID(&sde_crtc->base), en, sde_crtc->enabled, + sde_crtc->suspend, sde_crtc->vblank_requested); + if (sde_crtc->enabled && !sde_crtc->suspend) { + ret = _sde_crtc_vblank_enable_no_lock(sde_crtc, en); + if (ret) + SDE_ERROR("%s vblank enable failed: %d\n", + sde_crtc->name, ret); } + sde_crtc->vblank_requested = en; + mutex_unlock(&sde_crtc->crtc_lock); + return 0; } @@ -1608,8 +1834,7 @@ static int _sde_debugfs_status_show(struct seq_file *s, void *data) sde_crtc->vblank_cb_time = ktime_set(0, 0); } - seq_printf(s, "vblank_refcount:%d\n", - atomic_read(&sde_crtc->vblank_refcount)); + seq_printf(s, "vblank_enable:%d\n", sde_crtc->vblank_requested); mutex_unlock(&sde_crtc->crtc_lock); @@ -1737,8 +1962,8 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, crtc = &sde_crtc->base; crtc->dev = dev; - atomic_set(&sde_crtc->vblank_refcount, 0); + mutex_init(&sde_crtc->crtc_lock); spin_lock_init(&sde_crtc->spin_lock); atomic_set(&sde_crtc->frame_pending, 0); @@ -1760,7 +1985,6 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, snprintf(sde_crtc->name, SDE_CRTC_NAME_SIZE, "crtc%u", crtc->base.id); /* initialize output fence support */ - mutex_init(&sde_crtc->crtc_lock); sde_fence_init(&sde_crtc->output_fence, sde_crtc->name, crtc->base.id); /* initialize debugfs support */ diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h index aaa815c76c4e..0eed61580cd8 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.h +++ b/drivers/gpu/drm/msm/sde/sde_crtc.h @@ -81,7 +81,11 @@ struct sde_crtc_frame_event { * @debugfs_root : Parent of debugfs node * @vblank_cb_count : count of vblank callback since last reset * @vblank_cb_time : ktime at vblank count reset - * @vblank_refcount : reference count for vblank enable request + * @vblank_requested : whether the user has requested vblank events + * @suspend : whether or not a suspend operation is in progress + * @enabled : whether the SDE CRTC is currently enabled. updated in the + * commit-thread, not state-swap time which is earlier, so + * safe to make decisions on during VBLANK on/off work * @feature_list : list of color processing features supported on a crtc * @active_list : list of color processing features are active * @dirty_list : list of color processing features are dirty @@ -116,7 +120,9 @@ struct sde_crtc { u32 vblank_cb_count; ktime_t vblank_cb_time; - atomic_t vblank_refcount; + bool vblank_requested; + bool suspend; + bool enabled; struct list_head feature_list; struct list_head active_list; diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 97c9f8baea6d..cb8b349e72c7 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -80,6 +80,42 @@ static struct sde_csc_cfg sde_csc_10bit_convert[SDE_MAX_CSC] = { { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, }, + + [SDE_CSC_RGB2YUV_709L] = { + { + TO_S15D16(0x005d), TO_S15D16(0x013a), TO_S15D16(0x0020), + TO_S15D16(0xffcc), TO_S15D16(0xff53), TO_S15D16(0x00e1), + TO_S15D16(0x00e1), TO_S15D16(0xff34), TO_S15D16(0xffeb), + }, + { 0x0, 0x0, 0x0,}, + { 0x0040, 0x0200, 0x0200,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,}, + }, + + [SDE_CSC_RGB2YUV_2020L] = { + { + TO_S15D16(0x0073), TO_S15D16(0x0129), TO_S15D16(0x001a), + TO_S15D16(0xffc1), TO_S15D16(0xff5e), TO_S15D16(0x00e0), + TO_S15D16(0x00e0), TO_S15D16(0xff32), TO_S15D16(0xffee), + }, + { 0x0, 0x0, 0x0,}, + { 0x0040, 0x0200, 0x0200,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,}, + }, + + [SDE_CSC_RGB2YUV_2020FR] = { + { + TO_S15D16(0x0086), TO_S15D16(0x015b), TO_S15D16(0x001e), + TO_S15D16(0xffb9), TO_S15D16(0xff47), TO_S15D16(0x0100), + TO_S15D16(0x0100), TO_S15D16(0xff15), TO_S15D16(0xffeb), + }, + { 0x0, 0x0, 0x0,}, + { 0x0, 0x0200, 0x0200,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + }, }; /** @@ -470,11 +506,6 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) SDE_EVT32(DRMID(drm_enc)); - if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) { - SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id); - del_timer_sync(&sde_enc->frame_done_timer); - } - for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; @@ -487,6 +518,12 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) } } + /* after phys waits for frame-done, should be no more frames pending */ + if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) { + SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id); + del_timer_sync(&sde_enc->frame_done_timer); + } + if (sde_enc->cur_master && sde_enc->cur_master->ops.disable) sde_enc->cur_master->ops.disable(sde_enc->cur_master); @@ -826,7 +863,12 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc; struct sde_encoder_phys *phys; + struct drm_connector *conn_mas = NULL; unsigned int i; + enum sde_csc_type conn_csc; + struct drm_display_mode *mode; + struct sde_hw_cdm *hw_cdm; + int mode_is_yuv = 0; int rc; if (!drm_enc) { @@ -846,11 +888,46 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) } if (sde_enc->cur_master && sde_enc->cur_master->connector) { - rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector); + conn_mas = sde_enc->cur_master->connector; + rc = sde_connector_pre_kickoff(conn_mas); if (rc) - SDE_ERROR_ENC(sde_enc, "kickoff conn%d failed rc %d\n", - sde_enc->cur_master->connector->base.id, - rc); + SDE_ERROR_ENC(sde_enc, + "kickoff conn%d failed rc %d\n", + conn_mas->base.id, + rc); + + for (i = 0; i < sde_enc->num_phys_encs; i++) { + phys = sde_enc->phys_encs[i]; + if (phys) { + mode = &phys->cached_mode; + mode_is_yuv = (mode->private_flags & + MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420); + } + /** + * Check the CSC matrix type to which the + * CDM CSC matrix should be updated to based + * on the connector HDR state + */ + conn_csc = sde_connector_get_csc_type(conn_mas); + if (phys && mode_is_yuv) { + if (phys->enc_cdm_csc != conn_csc) { + hw_cdm = phys->hw_cdm; + rc = hw_cdm->ops.setup_csc_data(hw_cdm, + &sde_csc_10bit_convert[conn_csc]); + + if (rc) + SDE_ERROR_ENC(sde_enc, + "CSC setup failed rc %d\n", + rc); + SDE_DEBUG_ENC(sde_enc, + "updating CSC %d to %d\n", + phys->enc_cdm_csc, + conn_csc); + phys->enc_cdm_csc = conn_csc; + + } + } + } } } @@ -1417,6 +1494,7 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, struct sde_encoder_virt *sde_enc = NULL; struct sde_hw_cdm *hw_cdm = phys_enc->hw_cdm; struct sde_hw_cdm_cfg *cdm_cfg = &phys_enc->cdm_cfg; + struct drm_connector *connector = phys_enc->connector; int ret; u32 csc_type = 0; @@ -1476,10 +1554,26 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, cdm_cfg->h_cdwn_type, cdm_cfg->v_cdwn_type); - if (output_type == CDM_CDWN_OUTPUT_HDMI) - csc_type = SDE_CSC_RGB2YUV_601FR; - else if (output_type == CDM_CDWN_OUTPUT_WB) + /** + * Choose CSC matrix based on following rules: + * 1. If connector supports quantization select, + * pick Full-Range for better quality. + * 2. If non-CEA mode, then pick Full-Range as per CEA spec + * 3. Otherwise, pick Limited-Range as all other CEA modes + * need a limited range + */ + + if (output_type == CDM_CDWN_OUTPUT_HDMI) { + if (connector && connector->yuv_qs) + csc_type = SDE_CSC_RGB2YUV_601FR; + else if (connector && + sde_connector_mode_needs_full_range(connector)) + csc_type = SDE_CSC_RGB2YUV_601FR; + else + csc_type = SDE_CSC_RGB2YUV_601L; + } else if (output_type == CDM_CDWN_OUTPUT_WB) { csc_type = SDE_CSC_RGB2YUV_601L; + } if (hw_cdm && hw_cdm->ops.setup_csc_data) { ret = hw_cdm->ops.setup_csc_data(hw_cdm, @@ -1490,6 +1584,9 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, } } + /* Cache the CSC default matrix type */ + phys_enc->enc_cdm_csc = csc_type; + if (hw_cdm && hw_cdm->ops.setup_cdwn) { ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg); if (ret < 0) { diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h index 20f125155de3..aec844d640bd 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h @@ -174,6 +174,7 @@ enum sde_intr_idx { * @split_role: Role to play in a split-panel configuration * @intf_mode: Interface mode * @intf_idx: Interface index on sde hardware + * @enc_cdm_csc: Cached CSC type of CDM block * @enc_spinlock: Virtual-Encoder-Wide Spin Lock for IRQ purposes * @enable_state: Enable state tracking * @vblank_refcount: Reference count of vblank request @@ -201,6 +202,7 @@ struct sde_encoder_phys { enum sde_enc_split_role split_role; enum sde_intf_mode intf_mode; enum sde_intf intf_idx; + enum sde_csc_type enc_cdm_csc; spinlock_t *enc_spinlock; enum sde_enc_enable_state enable_state; atomic_t vblank_refcount; diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c index 78a8b732b0de..d58c06de1684 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c @@ -281,23 +281,40 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx) { struct sde_encoder_phys_vid *vid_enc = arg; struct sde_encoder_phys *phys_enc; + struct sde_hw_ctl *hw_ctl; unsigned long lock_flags; - int new_cnt; + u32 flush_register = 0; + int new_cnt = -1, old_cnt = -1; if (!vid_enc) return; phys_enc = &vid_enc->base; + hw_ctl = phys_enc->hw_ctl; + if (phys_enc->parent_ops.handle_vblank_virt) phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent, phys_enc); + old_cnt = atomic_read(&phys_enc->pending_kickoff_cnt); + + /* + * only decrement the pending flush count if we've actually flushed + * hardware. due to sw irq latency, vblank may have already happened + * so we need to double-check with hw that it accepted the flush bits + */ spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); - new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0); - SDE_EVT32_IRQ(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0, - new_cnt); + if (hw_ctl && hw_ctl->ops.get_flush_register) + flush_register = hw_ctl->ops.get_flush_register(hw_ctl); + + if (flush_register == 0) + new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, + -1, 0); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); + SDE_EVT32_IRQ(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0, + old_cnt, new_cnt, flush_register); + /* Signal any waiting atomic commit thread */ wake_up_all(&phys_enc->pending_kickoff_wq); } @@ -593,22 +610,33 @@ static void sde_encoder_phys_vid_get_hw_resources( struct drm_connector_state *conn_state) { struct sde_encoder_phys_vid *vid_enc; + struct sde_mdss_cfg *vid_catalog; if (!phys_enc || !hw_res) { SDE_ERROR("invalid arg(s), enc %d hw_res %d conn_state %d\n", - phys_enc != 0, hw_res != 0, conn_state != 0); + phys_enc != NULL, hw_res != NULL, conn_state != NULL); return; } + vid_catalog = phys_enc->sde_kms->catalog; vid_enc = to_sde_encoder_phys_vid(phys_enc); - if (!vid_enc->hw_intf) { - SDE_ERROR("invalid arg(s), hw_intf\n"); + if (!vid_enc->hw_intf || !vid_catalog) { + SDE_ERROR("invalid arg(s), hw_intf %d vid_catalog %d\n", + vid_enc->hw_intf != NULL, vid_catalog != NULL); return; } SDE_DEBUG_VIDENC(vid_enc, "\n"); + if (vid_enc->hw_intf->idx > INTF_MAX) { + SDE_ERROR("invalid arg(s), idx %d\n", + vid_enc->hw_intf->idx); + return; + } hw_res->intfs[vid_enc->hw_intf->idx - INTF_0] = INTF_MODE_VIDEO; - hw_res->needs_cdm = true; + + if (vid_catalog->intf[vid_enc->hw_intf->idx - INTF_0].type + == INTF_HDMI) + hw_res->needs_cdm = true; SDE_DEBUG_DRIVER("[vid] needs_cdm=%d\n", hw_res->needs_cdm); } @@ -689,6 +717,35 @@ static int sde_encoder_phys_vid_wait_for_commit_done( return ret; } +static void sde_encoder_phys_vid_prepare_for_kickoff( + struct sde_encoder_phys *phys_enc) +{ + struct sde_encoder_phys_vid *vid_enc; + struct sde_hw_ctl *ctl; + int rc; + + if (!phys_enc) { + SDE_ERROR("invalid encoder\n"); + return; + } + vid_enc = to_sde_encoder_phys_vid(phys_enc); + + ctl = phys_enc->hw_ctl; + if (!ctl || !ctl->ops.wait_reset_status) + return; + + /* + * hw supports hardware initiated ctl reset, so before we kickoff a new + * frame, need to check and wait for hw initiated ctl reset completion + */ + rc = ctl->ops.wait_reset_status(ctl); + if (rc) { + SDE_ERROR_VIDENC(vid_enc, "ctl %d reset failure: %d\n", + ctl->idx, rc); + SDE_DBG_DUMP("panic"); + } +} + static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc) { struct msm_drm_private *priv; @@ -821,6 +878,7 @@ static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops) ops->get_hw_resources = sde_encoder_phys_vid_get_hw_resources; ops->control_vblank_irq = sde_encoder_phys_vid_control_vblank_irq; ops->wait_for_commit_done = sde_encoder_phys_vid_wait_for_commit_done; + ops->prepare_for_kickoff = sde_encoder_phys_vid_prepare_for_kickoff; ops->handle_post_kickoff = sde_encoder_phys_vid_handle_post_kickoff; ops->needs_single_flush = sde_encoder_phys_vid_needs_single_flush; ops->setup_misr = sde_encoder_phys_vid_setup_misr; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 635c2e4e954b..1a563ea5f50c 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -134,6 +134,7 @@ enum { enum { VIG_QSEED_OFF, + VIG_QSEED_LEN, VIG_CSC_OFF, VIG_HSIC_PROP, VIG_MEMCOLOR_PROP, @@ -143,6 +144,7 @@ enum { enum { RGB_SCALER_OFF, + RGB_SCALER_LEN, RGB_PCC_PROP, RGB_PROP_MAX, }; @@ -301,6 +303,7 @@ static struct sde_prop_type sspp_prop[] = { static struct sde_prop_type vig_prop[] = { {VIG_QSEED_OFF, "qcom,sde-vig-qseed-off", false, PROP_TYPE_U32}, + {VIG_QSEED_LEN, "qcom,sde-vig-qseed-size", false, PROP_TYPE_U32}, {VIG_CSC_OFF, "qcom,sde-vig-csc-off", false, PROP_TYPE_U32}, {VIG_HSIC_PROP, "qcom,sde-vig-hsic", false, PROP_TYPE_U32_ARRAY}, {VIG_MEMCOLOR_PROP, "qcom,sde-vig-memcolor", false, @@ -310,6 +313,7 @@ static struct sde_prop_type vig_prop[] = { static struct sde_prop_type rgb_prop[] = { {RGB_SCALER_OFF, "qcom,sde-rgb-scaler-off", false, PROP_TYPE_U32}, + {RGB_SCALER_LEN, "qcom,sde-rgb-scaler-size", false, PROP_TYPE_U32}, {RGB_PCC_PROP, "qcom,sde-rgb-pcc", false, PROP_TYPE_U32_ARRAY}, }; @@ -689,7 +693,10 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, { sblk->maxupscale = MAX_SSPP_UPSCALE; sblk->maxdwnscale = MAX_SSPP_DOWNSCALE; + sblk->format_list = plane_formats_yuv; sspp->id = SSPP_VIG0 + *vig_count; + snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", + sspp->id - SSPP_VIG0); sspp->clk_ctrl = SDE_CLK_CTRL_VIG0 + *vig_count; sspp->type = SSPP_TYPE_VIG; set_bit(SDE_SSPP_QOS, &sspp->features); @@ -703,14 +710,24 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED2; sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value, VIG_QSEED_OFF, 0); - } else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) { + sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value, + VIG_QSEED_LEN, 0); + snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN, + "sspp_scaler%u", sspp->id - SSPP_VIG0); + } else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) { set_bit(SDE_SSPP_SCALER_QSEED3, &sspp->features); sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3; sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value, VIG_QSEED_OFF, 0); + sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value, + VIG_QSEED_LEN, 0); + snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN, + "sspp_scaler%u", sspp->id - SSPP_VIG0); } sblk->csc_blk.id = SDE_SSPP_CSC; + snprintf(sblk->csc_blk.name, SDE_HW_BLK_NAME_LEN, + "sspp_csc%u", sspp->id - SSPP_VIG0); if (sde_cfg->csc_type == SDE_SSPP_CSC) { set_bit(SDE_SSPP_CSC, &sspp->features); sblk->csc_blk.base = PROP_VALUE_ACCESS(prop_value, @@ -722,6 +739,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, } sblk->hsic_blk.id = SDE_SSPP_HSIC; + snprintf(sblk->hsic_blk.name, SDE_HW_BLK_NAME_LEN, + "sspp_hsic%u", sspp->id - SSPP_VIG0); if (prop_exists[VIG_HSIC_PROP]) { sblk->hsic_blk.base = PROP_VALUE_ACCESS(prop_value, VIG_HSIC_PROP, 0); @@ -732,6 +751,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, } sblk->memcolor_blk.id = SDE_SSPP_MEMCOLOR; + snprintf(sblk->memcolor_blk.name, SDE_HW_BLK_NAME_LEN, + "sspp_memcolor%u", sspp->id - SSPP_VIG0); if (prop_exists[VIG_MEMCOLOR_PROP]) { sblk->memcolor_blk.base = PROP_VALUE_ACCESS(prop_value, VIG_MEMCOLOR_PROP, 0); @@ -742,6 +763,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, } sblk->pcc_blk.id = SDE_SSPP_PCC; + snprintf(sblk->pcc_blk.name, SDE_HW_BLK_NAME_LEN, + "sspp_pcc%u", sspp->id - SSPP_VIG0); if (prop_exists[VIG_PCC_PROP]) { sblk->pcc_blk.base = PROP_VALUE_ACCESS(prop_value, VIG_PCC_PROP, 0); @@ -759,7 +782,10 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, { sblk->maxupscale = MAX_SSPP_UPSCALE; sblk->maxdwnscale = MAX_SSPP_DOWNSCALE; + sblk->format_list = plane_formats; sspp->id = SSPP_RGB0 + *rgb_count; + snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", + sspp->id - SSPP_VIG0); sspp->clk_ctrl = SDE_CLK_CTRL_RGB0 + *rgb_count; sspp->type = SSPP_TYPE_RGB; set_bit(SDE_SSPP_QOS, &sspp->features); @@ -773,11 +799,19 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED2; sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value, RGB_SCALER_OFF, 0); + sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value, + RGB_SCALER_LEN, 0); + snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN, + "sspp_scaler%u", sspp->id - SSPP_VIG0); } else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) { set_bit(SDE_SSPP_SCALER_RGB, &sspp->features); sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3; sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value, - RGB_SCALER_OFF, 0); + RGB_SCALER_LEN, 0); + sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value, + SSPP_SCALE_SIZE, 0); + snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN, + "sspp_scaler%u", sspp->id - SSPP_VIG0); } sblk->pcc_blk.id = SDE_SSPP_PCC; @@ -799,7 +833,10 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg, set_bit(SDE_SSPP_CURSOR, &sspp->features); sblk->maxupscale = SSPP_UNITY_SCALE; sblk->maxdwnscale = SSPP_UNITY_SCALE; + sblk->format_list = cursor_formats; sspp->id = SSPP_CURSOR0 + *cursor_count; + snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", + sspp->id - SSPP_VIG0); sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count; sspp->type = SSPP_TYPE_CURSOR; (*cursor_count)++; @@ -812,9 +849,12 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg, { sblk->maxupscale = SSPP_UNITY_SCALE; sblk->maxdwnscale = SSPP_UNITY_SCALE; + sblk->format_list = plane_formats; sspp->id = SSPP_DMA0 + *dma_count; sspp->clk_ctrl = SDE_CLK_CTRL_DMA0 + *dma_count; sspp->type = SSPP_TYPE_DMA; + snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", + sspp->id - SSPP_VIG0); set_bit(SDE_SSPP_QOS, &sspp->features); (*dma_count)++; snprintf(sspp->name, sizeof(sspp->name), "dma%d", *dma_count-1); @@ -913,6 +953,7 @@ static int sde_sspp_parse_dt(struct device_node *np, sspp->sblk = sblk; sspp->base = PROP_VALUE_ACCESS(prop_value, SSPP_OFF, i); + sspp->len = PROP_VALUE_ACCESS(prop_value, SSPP_SIZE, 0); sblk->maxlinewidth = sde_cfg->max_sspp_linewidth; set_bit(SDE_SSPP_SRC, &sspp->features); @@ -940,6 +981,9 @@ static int sde_sspp_parse_dt(struct device_node *np, goto end; } + snprintf(sblk->src_blk.name, SDE_HW_BLK_NAME_LEN, "sspp_src_%u", + sspp->id - SSPP_VIG0); + sblk->maxhdeciexp = MAX_HORZ_DECIMATION; sblk->maxvdeciexp = MAX_VERT_DECIMATION; @@ -1029,7 +1073,10 @@ static int sde_ctl_parse_dt(struct device_node *np, for (i = 0; i < off_count; i++) { ctl = sde_cfg->ctl + i; ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i); + ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0); ctl->id = CTL_0 + i; + snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u", + ctl->id - CTL_0); if (i < MAX_SPLIT_DISPLAY_CTL) set_bit(SDE_CTL_SPLIT_DISPLAY, &ctl->features); @@ -1121,6 +1168,9 @@ static int sde_mixer_parse_dt(struct device_node *np, mixer->base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i); mixer->len = PROP_VALUE_ACCESS(prop_value, MIXER_LEN, 0); mixer->id = LM_0 + i; + snprintf(mixer->name, SDE_HW_BLK_NAME_LEN, "lm_%u", + mixer->id - LM_0); + if (!prop_exists[MIXER_LEN]) mixer->len = DEFAULT_SDE_HW_BLOCK_LEN; @@ -1207,6 +1257,9 @@ static int sde_intf_parse_dt(struct device_node *np, intf->base = PROP_VALUE_ACCESS(prop_value, INTF_OFF, i); intf->len = PROP_VALUE_ACCESS(prop_value, INTF_LEN, 0); intf->id = INTF_0 + i; + snprintf(intf->name, SDE_HW_BLK_NAME_LEN, "intf_%u", + intf->id - INTF_0); + if (!prop_exists[INTF_LEN]) intf->len = DEFAULT_SDE_HW_BLOCK_LEN; @@ -1286,11 +1339,14 @@ static int sde_wb_parse_dt(struct device_node *np, wb->base = PROP_VALUE_ACCESS(prop_value, WB_OFF, i); wb->id = WB_0 + PROP_VALUE_ACCESS(prop_value, WB_ID, i); + snprintf(wb->name, SDE_HW_BLK_NAME_LEN, "wb_%u", + wb->id - WB_0); wb->clk_ctrl = SDE_CLK_CTRL_WB0 + PROP_VALUE_ACCESS(prop_value, WB_ID, i); wb->xin_id = PROP_VALUE_ACCESS(prop_value, WB_XIN_ID, i); wb->vbif_idx = VBIF_NRT; wb->len = PROP_VALUE_ACCESS(prop_value, WB_LEN, 0); + wb->format_list = wb2_formats; if (!prop_exists[WB_LEN]) wb->len = DEFAULT_SDE_HW_BLOCK_LEN; sblk->maxlinewidth = sde_cfg->max_wb_linewidth; @@ -1510,7 +1566,10 @@ static int sde_dspp_parse_dt(struct device_node *np, for (i = 0; i < off_count; i++) { dspp = sde_cfg->dspp + i; dspp->base = PROP_VALUE_ACCESS(prop_value, DSPP_OFF, i); + dspp->len = PROP_VALUE_ACCESS(prop_value, DSPP_SIZE, 0); dspp->id = DSPP_0 + i; + snprintf(dspp->name, SDE_HW_BLK_NAME_LEN, "dspp_%u", + dspp->id - DSPP_0); sblk = kzalloc(sizeof(*sblk), GFP_KERNEL); if (!sblk) { @@ -1580,6 +1639,8 @@ static int sde_cdm_parse_dt(struct device_node *np, cdm = sde_cfg->cdm + i; cdm->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i); cdm->id = CDM_0 + i; + snprintf(cdm->name, SDE_HW_BLK_NAME_LEN, "cdm_%u", + cdm->id - CDM_0); cdm->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0); /* intf3 and wb2 for cdm block */ @@ -1645,6 +1706,8 @@ static int sde_vbif_parse_dt(struct device_node *np, vbif->base = PROP_VALUE_ACCESS(prop_value, VBIF_OFF, i); vbif->len = vbif_len; vbif->id = VBIF_0 + PROP_VALUE_ACCESS(prop_value, VBIF_ID, i); + snprintf(vbif->name, SDE_HW_BLK_NAME_LEN, "vbif_%u", + vbif->id - VBIF_0); SDE_DEBUG("vbif:%d\n", vbif->id - VBIF_0); @@ -1772,15 +1835,21 @@ static int sde_pp_parse_dt(struct device_node *np, pp->base = PROP_VALUE_ACCESS(prop_value, PP_OFF, i); pp->id = PINGPONG_0 + i; + snprintf(pp->name, SDE_HW_BLK_NAME_LEN, "pingpong_%u", + pp->id - PINGPONG_0); pp->len = PROP_VALUE_ACCESS(prop_value, PP_LEN, 0); sblk->te.base = PROP_VALUE_ACCESS(prop_value, TE_OFF, i); sblk->te.id = SDE_PINGPONG_TE; + snprintf(sblk->te.name, SDE_HW_BLK_NAME_LEN, "te_%u", + pp->id - PINGPONG_0); set_bit(SDE_PINGPONG_TE, &pp->features); sblk->te2.base = PROP_VALUE_ACCESS(prop_value, TE2_OFF, i); if (sblk->te2.base) { sblk->te2.id = SDE_PINGPONG_TE2; + snprintf(sblk->te2.name, SDE_HW_BLK_NAME_LEN, "te2_%u", + pp->id - PINGPONG_0); set_bit(SDE_PINGPONG_TE2, &pp->features); set_bit(SDE_PINGPONG_SPLIT, &pp->features); } @@ -1791,6 +1860,8 @@ static int sde_pp_parse_dt(struct device_node *np, sblk->dsc.base = PROP_VALUE_ACCESS(prop_value, DSC_OFF, i); if (sblk->dsc.base) { sblk->dsc.id = SDE_PINGPONG_DSC; + snprintf(sblk->dsc.name, SDE_HW_BLK_NAME_LEN, "dsc_%u", + sblk->dsc.id - PINGPONG_0); set_bit(SDE_PINGPONG_DSC, &pp->features); } } @@ -1921,9 +1992,13 @@ static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) cfg->mdss_count = 1; cfg->mdss[0].base = MDSS_BASE_OFFSET; cfg->mdss[0].id = MDP_TOP; + snprintf(cfg->mdss[0].name, SDE_HW_BLK_NAME_LEN, "mdss_%u", + cfg->mdss[0].id - MDP_TOP); cfg->mdp_count = 1; cfg->mdp[0].id = MDP_TOP; + snprintf(cfg->mdp[0].name, SDE_HW_BLK_NAME_LEN, "top_%u", + cfg->mdp[0].id - MDP_TOP); cfg->mdp[0].base = PROP_VALUE_ACCESS(prop_value, SDE_OFF, 0); cfg->mdp[0].len = PROP_VALUE_ACCESS(prop_value, SDE_LEN, 0); if (!prop_exists[SDE_LEN]) diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h index 73bb77b7afa6..81e6bfe6defe 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h @@ -44,10 +44,12 @@ #define SDE_HW_VER_172 SDE_HW_VER(1, 7, 2) /* 8996 v3.0 */ #define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */ #define SDE_HW_VER_301 SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */ -#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* msmskunk v1.0 */ +#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* sdm845 v1.0 */ #define IS_MSMSKUNK_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400) +#define SDE_HW_BLK_NAME_LEN 16 + #define MAX_IMG_WIDTH 0x3fff #define MAX_IMG_HEIGHT 0x3fff @@ -58,8 +60,6 @@ #define SDE_COLOR_PROCESS_MAJOR(version) (((version) & 0xFFFF0000) >> 16) #define SDE_COLOR_PROCESS_MINOR(version) ((version) & 0xFFFF) -#define SSPP_NAME_SIZE 12 - /** * MDP TOP BLOCK features * @SDE_MDP_PANIC_PER_PIPE Panic configuration needs to be be done per pipe @@ -236,12 +236,14 @@ enum { /** * MACRO SDE_HW_BLK_INFO - information of HW blocks inside SDE + * @name: string name for debug purposes * @id: enum identifying this block * @base: register base offset to mdss * @len: length of hardware block * @features bit mask identifying sub-blocks/features */ #define SDE_HW_BLK_INFO \ + char name[SDE_HW_BLK_NAME_LEN]; \ u32 id; \ u32 base; \ u32 len; \ @@ -249,12 +251,14 @@ enum { /** * MACRO SDE_HW_SUBBLK_INFO - information of HW sub-block inside SDE + * @name: string name for debug purposes * @id: enum identifying this sub-block * @base: offset of this sub-block relative to the block * offset * @len register block length of this sub-block */ #define SDE_HW_SUBBLK_INFO \ + char name[SDE_HW_BLK_NAME_LEN]; \ u32 id; \ u32 base; \ u32 len @@ -458,7 +462,6 @@ struct sde_ctl_cfg { * @sblk: SSPP sub-blocks information * @xin_id: bus client identifier * @clk_ctrl clock control identifier - * @name source pipe name * @type sspp type identifier */ struct sde_sspp_cfg { @@ -466,7 +469,6 @@ struct sde_sspp_cfg { const struct sde_sspp_sub_blks *sblk; u32 xin_id; enum sde_clk_ctrl_type clk_ctrl; - char name[SSPP_NAME_SIZE]; u32 type; }; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c index 9ec81c227e60..da04be4e9719 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c @@ -14,6 +14,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_cdm.h" +#include "sde_dbg.h" #define CDM_CSC_10_OPMODE 0x000 #define CDM_CSC_10_BASE 0x004 @@ -295,6 +296,9 @@ struct sde_hw_cdm *sde_hw_cdm_init(enum sde_cdm idx, */ sde_hw_cdm_setup_csc_10bit(c, &rgb2yuv_cfg); + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c index 56d9f2a4a9b8..270e79a774b2 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c @@ -13,6 +13,7 @@ #include <linux/delay.h> #include "sde_hwio.h" #include "sde_hw_ctl.h" +#include "sde_dbg.h" #define CTL_LAYER(lm) \ (((lm) == LM_5) ? (0x024) : (((lm) - LM_0) * 0x004)) @@ -39,6 +40,7 @@ static struct sde_ctl_cfg *_ctl_offset(enum sde_ctl ctl, if (ctl == m->ctl[i].id) { b->base_off = addr; b->blk_off = m->ctl[i].base; + b->length = m->ctl[i].len; b->hwversion = m->hwversion; b->log_mask = SDE_DBG_MASK_CTL; return &m->ctl[i]; @@ -92,6 +94,12 @@ static inline void sde_hw_ctl_trigger_flush(struct sde_hw_ctl *ctx) SDE_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask); } +static inline u32 sde_hw_ctl_get_flush_register(struct sde_hw_ctl *ctx) +{ + struct sde_hw_blk_reg_map *c = &ctx->hw; + + return SDE_REG_READ(c, CTL_FLUSH); +} static inline uint32_t sde_hw_ctl_get_bitmask_sspp(struct sde_hw_ctl *ctx, enum sde_sspp sspp) @@ -247,23 +255,58 @@ static inline int sde_hw_ctl_get_bitmask_cdm(struct sde_hw_ctl *ctx, return 0; } +static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 count) +{ + struct sde_hw_blk_reg_map *c = &ctx->hw; + u32 status; + + /* protect to do at least one iteration */ + if (!count) + count = 1; + + /* + * it takes around 30us to have mdp finish resetting its ctl path + * poll every 50us so that reset should be completed at 1st poll + */ + do { + status = SDE_REG_READ(c, CTL_SW_RESET); + status &= 0x01; + if (status) + usleep_range(20, 50); + } while (status && --count > 0); + + return status; +} + static int sde_hw_ctl_reset_control(struct sde_hw_ctl *ctx) { struct sde_hw_blk_reg_map *c = &ctx->hw; - int count = SDE_REG_RESET_TIMEOUT_COUNT; - int reset; + pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx); SDE_REG_WRITE(c, CTL_SW_RESET, 0x1); + if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT)) + return -EINVAL; - for (; count > 0; count--) { - /* insert small delay to avoid spinning the cpu while waiting */ - usleep_range(20, 50); - reset = SDE_REG_READ(c, CTL_SW_RESET); - if (reset == 0) - return 0; + return 0; +} + +static int sde_hw_ctl_wait_reset_status(struct sde_hw_ctl *ctx) +{ + struct sde_hw_blk_reg_map *c = &ctx->hw; + u32 status; + + status = SDE_REG_READ(c, CTL_SW_RESET); + status &= 0x01; + if (!status) + return 0; + + pr_debug("hw ctl reset is set for ctl:%d\n", ctx->idx); + if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT)) { + pr_err("hw recovery is not complete for ctl:%d\n", ctx->idx); + return -EINVAL; } - return -EINVAL; + return 0; } static void sde_hw_ctl_clear_all_blendstages(struct sde_hw_ctl *ctx) @@ -415,9 +458,11 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops, ops->update_pending_flush = sde_hw_ctl_update_pending_flush; ops->get_pending_flush = sde_hw_ctl_get_pending_flush; ops->trigger_flush = sde_hw_ctl_trigger_flush; + ops->get_flush_register = sde_hw_ctl_get_flush_register; ops->trigger_start = sde_hw_ctl_trigger_start; ops->setup_intf_cfg = sde_hw_ctl_intf_cfg; ops->reset = sde_hw_ctl_reset_control; + ops->wait_reset_status = sde_hw_ctl_wait_reset_status; ops->clear_all_blendstages = sde_hw_ctl_clear_all_blendstages; ops->setup_blendstage = sde_hw_ctl_setup_blendstage; ops->get_bitmask_sspp = sde_hw_ctl_get_bitmask_sspp; @@ -452,6 +497,9 @@ struct sde_hw_ctl *sde_hw_ctl_init(enum sde_ctl idx, c->mixer_count = m->mixer_count; c->mixer_hw_caps = m->mixer; + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h index 2fb7b377e51d..74dbde92639a 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -94,6 +94,13 @@ struct sde_hw_ctl_ops { void (*trigger_flush)(struct sde_hw_ctl *ctx); /** + * Read the value of the flush register + * @ctx : ctl path ctx pointer + * @Return: value of the ctl flush register. + */ + u32 (*get_flush_register)(struct sde_hw_ctl *ctx); + + /** * Setup ctl_path interface config * @ctx * @cfg : interface config structure pointer @@ -103,6 +110,17 @@ struct sde_hw_ctl_ops { int (*reset)(struct sde_hw_ctl *c); + /* + * wait_reset_status - checks ctl reset status + * @ctx : ctl path ctx pointer + * + * This function checks the ctl reset status bit. + * If the reset bit is set, it keeps polling the status till the hw + * reset is complete. + * Returns: 0 on success or -error if reset incomplete within interval + */ + int (*wait_reset_status)(struct sde_hw_ctl *ctx); + uint32_t (*get_bitmask_sspp)(struct sde_hw_ctl *ctx, enum sde_sspp blk); diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c index d6250b07b4f0..2fd879a0030d 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,6 +15,7 @@ #include "sde_hw_catalog.h" #include "sde_hw_dspp.h" #include "sde_hw_color_processing.h" +#include "sde_dbg.h" static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp, struct sde_mdss_cfg *m, @@ -27,6 +28,7 @@ static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp, if (dspp == m->dspp[i].id) { b->base_off = addr; b->blk_off = m->dspp[i].base; + b->length = m->dspp[i].len; b->hwversion = m->hwversion; b->log_mask = SDE_DBG_MASK_DSPP; return &m->dspp[i]; @@ -111,6 +113,9 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx, c->cap = cfg; _setup_dspp_ops(c, c->cap->features); + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c index 042b0ee7909a..9e1b97800cb9 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,6 +13,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_intf.h" +#include "sde_dbg.h" #define INTF_TIMING_ENGINE_EN 0x000 #define INTF_CONFIG 0x004 @@ -83,6 +84,7 @@ static struct sde_intf_cfg *_intf_offset(enum sde_intf intf, (m->intf[i].type != INTF_NONE)) { b->base_off = addr; b->blk_off = m->intf[i].base; + b->length = m->intf[i].len; b->hwversion = m->hwversion; b->log_mask = SDE_DBG_MASK_INTF; return &m->intf[i]; @@ -324,9 +326,9 @@ struct sde_hw_intf *sde_hw_intf_init(enum sde_intf idx, c->mdss = m; _setup_intf_ops(&c->ops, c->cap->features); - /* - * Perform any default initialization for the intf - */ + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c index 365b9b17715d..8b4e0901458f 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -14,6 +14,7 @@ #include "sde_hwio.h" #include "sde_hw_lm.h" #include "sde_hw_mdss.h" +#include "sde_dbg.h" #define LM_OP_MODE 0x00 #define LM_OUT_SIZE 0x04 @@ -37,6 +38,7 @@ static struct sde_lm_cfg *_lm_offset(enum sde_lm mixer, if (mixer == m->mixer[i].id) { b->base_off = addr; b->blk_off = m->mixer[i].base; + b->length = m->mixer[i].len; b->hwversion = m->hwversion; b->log_mask = SDE_DBG_MASK_LM; return &m->mixer[i]; @@ -195,9 +197,9 @@ struct sde_hw_mixer *sde_hw_lm_init(enum sde_lm idx, c->cap = cfg; _setup_mixer_ops(m, &c->ops, c->cap->features); - /* - * Perform any default initialization for the sspp blocks - */ + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h index 1edeff6a7aec..3d63d01a6d4e 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h @@ -18,6 +18,8 @@ #include "msm_drv.h" +#define SDE_DBG_NAME "sde" + #define SDE_NONE 0 #ifndef SDE_CSC_MATRIX_COEFF_SIZE @@ -344,6 +346,9 @@ enum sde_3d_blend_mode { enum sde_csc_type { SDE_CSC_RGB2YUV_601L, SDE_CSC_RGB2YUV_601FR, + SDE_CSC_RGB2YUV_709L, + SDE_CSC_RGB2YUV_2020L, + SDE_CSC_RGB2YUV_2020FR, SDE_MAX_CSC }; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c index 837edeeba4c6..8488d03af79a 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -14,6 +14,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_pingpong.h" +#include "sde_dbg.h" #define PP_TEAR_CHECK_EN 0x000 #define PP_SYNC_CONFIG_VSYNC 0x004 @@ -47,6 +48,7 @@ static struct sde_pingpong_cfg *_pingpong_offset(enum sde_pingpong pp, if (pp == m->pingpong[i].id) { b->base_off = addr; b->blk_off = m->pingpong[i].base; + b->length = m->pingpong[i].len; b->hwversion = m->hwversion; b->log_mask = SDE_DBG_MASK_PINGPONG; return &m->pingpong[i]; @@ -159,6 +161,9 @@ struct sde_hw_pingpong *sde_hw_pingpong_init(enum sde_pingpong idx, c->pingpong_hw_cap = cfg; _setup_pingpong_ops(&c->ops, c->pingpong_hw_cap->features); + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c index ea2890d776ae..be620aebf850 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c @@ -15,6 +15,7 @@ #include "sde_hw_lm.h" #include "sde_hw_sspp.h" #include "sde_hw_color_processing.h" +#include "sde_dbg.h" #define SDE_FETCH_CONFIG_RESET_VALUE 0x00000087 @@ -903,6 +904,7 @@ static struct sde_sspp_cfg *_sspp_offset(enum sde_sspp sspp, if (sspp == catalog->sspp[i].id) { b->base_off = addr; b->blk_off = catalog->sspp[i].base; + b->length = catalog->sspp[i].len; b->hwversion = catalog->hwversion; b->log_mask = SDE_DBG_MASK_SSPP; return &catalog->sspp[i]; @@ -917,26 +919,39 @@ struct sde_hw_pipe *sde_hw_sspp_init(enum sde_sspp idx, void __iomem *addr, struct sde_mdss_cfg *catalog) { - struct sde_hw_pipe *ctx; + struct sde_hw_pipe *hw_pipe; struct sde_sspp_cfg *cfg; - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) + hw_pipe = kzalloc(sizeof(*hw_pipe), GFP_KERNEL); + if (!hw_pipe) return ERR_PTR(-ENOMEM); - cfg = _sspp_offset(idx, addr, catalog, &ctx->hw); + cfg = _sspp_offset(idx, addr, catalog, &hw_pipe->hw); if (IS_ERR_OR_NULL(cfg)) { - kfree(ctx); + kfree(hw_pipe); return ERR_PTR(-EINVAL); } /* Assign ops */ - ctx->idx = idx; - ctx->cap = cfg; - _setup_layer_ops(ctx, ctx->cap->features); - ctx->highest_bank_bit = catalog->mdp[0].highest_bank_bit; - - return ctx; + hw_pipe->idx = idx; + hw_pipe->cap = cfg; + _setup_layer_ops(hw_pipe, hw_pipe->cap->features); + hw_pipe->highest_bank_bit = catalog->mdp[0].highest_bank_bit; + + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, + hw_pipe->hw.blk_off, + hw_pipe->hw.blk_off + hw_pipe->hw.length, + hw_pipe->hw.xin_id); + + if (cfg->sblk->scaler_blk.len) + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, + cfg->sblk->scaler_blk.name, + hw_pipe->hw.blk_off + cfg->sblk->scaler_blk.base, + hw_pipe->hw.blk_off + cfg->sblk->scaler_blk.base + + cfg->sblk->scaler_blk.len, + hw_pipe->hw.xin_id); + + return hw_pipe; } void sde_hw_sspp_destroy(struct sde_hw_pipe *ctx) diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c index d6d2e41ff5aa..218797e623a2 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_top.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,6 +13,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_top.h" +#include "sde_dbg.h" #define SSPP_SPARE 0x28 @@ -225,6 +226,7 @@ static const struct sde_mdp_cfg *_top_offset(enum sde_mdp mdp, if (mdp == m->mdp[i].id) { b->base_off = addr; b->blk_off = m->mdp[i].base; + b->length = m->mdp[i].len; b->hwversion = m->hwversion; b->log_mask = SDE_DBG_MASK_TOP; return &m->mdp[i]; @@ -258,9 +260,10 @@ struct sde_hw_mdp *sde_hw_mdptop_init(enum sde_mdp idx, mdp->cap = cfg; _setup_mdp_ops(&mdp->ops, mdp->cap->features); - /* - * Perform any default initialization for the intf - */ + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, + mdp->hw.blk_off, mdp->hw.blk_off + mdp->hw.length, + mdp->hw.xin_id); + sde_dbg_set_sde_top_offset(mdp->hw.blk_off); return mdp; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.h b/drivers/gpu/drm/msm/sde/sde_hw_util.h index c38c22237a57..008b657966b6 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_util.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_util.h @@ -24,12 +24,14 @@ * @base_off: mdp register mapped offset * @blk_off: pipe offset relative to mdss offset * @length length of register block offset + * @xin_id xin id * @hwversion mdss hw version number */ struct sde_hw_blk_reg_map { void __iomem *base_off; u32 blk_off; u32 length; + u32 xin_id; u32 hwversion; u32 log_mask; }; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c index 76473fa879c5..048ec47d7c72 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,6 +13,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_vbif.h" +#include "sde_dbg.h" #define VBIF_VERSION 0x0000 #define VBIF_CLK_FORCE_CTRL0 0x0008 @@ -123,6 +124,7 @@ static const struct sde_vbif_cfg *_top_offset(enum sde_vbif vbif, if (vbif == m->vbif[i].id) { b->base_off = addr; b->blk_off = m->vbif[i].base; + b->length = m->vbif[i].len; b->hwversion = m->hwversion; b->log_mask = SDE_DBG_MASK_VBIF; return &m->vbif[i]; @@ -156,6 +158,8 @@ struct sde_hw_vbif *sde_hw_vbif_init(enum sde_vbif idx, c->cap = cfg; _setup_vbif_ops(&c->ops, c->cap->features); + /* no need to register sub-range in sde dbg, dump entire vbif io base */ + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.c b/drivers/gpu/drm/msm/sde/sde_hw_wb.c index 426e9991a6b5..320b05f67669 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_wb.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,6 +15,7 @@ #include "sde_hw_catalog.h" #include "sde_hw_wb.h" #include "sde_formats.h" +#include "sde_dbg.h" #define WB_DST_FORMAT 0x000 #define WB_DST_OP_MODE 0x004 @@ -57,6 +58,7 @@ static struct sde_wb_cfg *_wb_offset(enum sde_wb wb, if (wb == m->wb[i].id) { b->base_off = addr; b->blk_off = m->wb[i].base; + b->length = m->wb[i].len; b->hwversion = m->hwversion; b->log_mask = SDE_DBG_MASK_WB; return &m->wb[i]; @@ -215,6 +217,9 @@ struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx, c->highest_bank_bit = m->mdp[0].highest_bank_bit; c->hw_mdp = hw_mdp; + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index 031493aa42b8..34a32d79f22c 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -328,24 +328,12 @@ static int sde_debugfs_danger_init(struct sde_kms *sde_kms, static int sde_kms_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) { - struct sde_kms *sde_kms = to_sde_kms(kms); - struct drm_device *dev = sde_kms->dev; - struct msm_drm_private *priv = dev->dev_private; - - sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true); - return sde_crtc_vblank(crtc, true); } static void sde_kms_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) { - struct sde_kms *sde_kms = to_sde_kms(kms); - struct drm_device *dev = sde_kms->dev; - struct msm_drm_private *priv = dev->dev_private; - sde_crtc_vblank(crtc, false); - - sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false); } static void sde_kms_prepare_commit(struct msm_kms *kms, @@ -355,6 +343,9 @@ static void sde_kms_prepare_commit(struct msm_kms *kms, struct drm_device *dev = sde_kms->dev; struct msm_drm_private *priv = dev->dev_private; + if (sde_kms->splash_info.handoff) + sde_splash_clean_up_exit_lk(kms); + sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true); } @@ -603,6 +594,8 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .set_property = sde_hdmi_set_property, .get_property = sde_hdmi_get_property, .pre_kickoff = sde_hdmi_pre_kickoff, + .mode_needs_full_range = sde_hdmi_mode_needs_full_range, + .get_csc_type = sde_hdmi_get_csc_type }; struct msm_display_info info = {0}; struct drm_encoder *encoder; @@ -997,8 +990,15 @@ static void _sde_kms_hw_destroy(struct sde_kms *sde_kms, sde_hw_catalog_deinit(sde_kms->catalog); sde_kms->catalog = NULL; + if (sde_kms->splash_info.handoff) { + if (sde_kms->core_client) + sde_splash_destroy(&sde_kms->splash_info, + &priv->phandle, sde_kms->core_client); + } + if (sde_kms->core_client) - sde_power_client_destroy(&priv->phandle, sde_kms->core_client); + sde_power_client_destroy(&priv->phandle, + sde_kms->core_client); sde_kms->core_client = NULL; if (sde_kms->vbif[VBIF_NRT]) @@ -1110,6 +1110,24 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms) continue; } + /* Attaching smmu means IOMMU HW starts to work immediately. + * However, display HW in LK is still accessing memory + * while the memory map is not done yet. + * So first set DOMAIN_ATTR_EARLY_MAP attribute 1 to bypass + * stage 1 translation in IOMMU HW. + */ + if ((i == MSM_SMMU_DOMAIN_UNSECURE) && + sde_kms->splash_info.handoff) { + ret = mmu->funcs->set_property(mmu, + DOMAIN_ATTR_EARLY_MAP, + &sde_kms->splash_info.handoff); + if (ret) { + SDE_ERROR("failed to set map att: %d\n", ret); + mmu->funcs->destroy(mmu); + goto fail; + } + } + aspace = msm_gem_smmu_address_space_create(sde_kms->dev->dev, mmu, "sde"); if (IS_ERR(aspace)) { @@ -1127,6 +1145,19 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms) goto fail; } + /* + * It's safe now to map the physical memory blcok LK accesses. + */ + if ((i == MSM_SMMU_DOMAIN_UNSECURE) && + sde_kms->splash_info.handoff) { + ret = sde_splash_smmu_map(sde_kms->dev, mmu, + &sde_kms->splash_info); + if (ret) { + SDE_ERROR("map rsv mem failed: %d\n", ret); + msm_gem_address_space_put(aspace); + goto fail; + } + } } return 0; @@ -1136,11 +1167,50 @@ fail: return ret; } +static void __iomem *_sde_kms_ioremap(struct platform_device *pdev, + const char *name, unsigned long *out_size) +{ + struct resource *res; + unsigned long size; + void __iomem *ptr; + + if (out_size) + *out_size = 0; + + if (name) + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + else + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + /* availability depends on platform */ + SDE_DEBUG("failed to get memory resource: %s\n", name); + return NULL; + } + + size = resource_size(res); + + ptr = devm_ioremap_nocache(&pdev->dev, res->start, size); + if (!ptr) { + SDE_ERROR("failed to ioremap: %s\n", name); + return NULL; + } + + SDE_DEBUG("IO:region %s %pK %08lx\n", name, ptr, size); + + if (out_size) + *out_size = size; + + return ptr; +} + + static int sde_kms_hw_init(struct msm_kms *kms) { struct sde_kms *sde_kms; struct drm_device *dev; struct msm_drm_private *priv; + struct sde_splash_info *sinfo; int i, rc = -EINVAL; if (!kms) { @@ -1161,29 +1231,42 @@ static int sde_kms_hw_init(struct msm_kms *kms) goto end; } - sde_kms->mmio = msm_ioremap(dev->platformdev, "mdp_phys", "SDE"); - if (IS_ERR(sde_kms->mmio)) { - rc = PTR_ERR(sde_kms->mmio); - SDE_ERROR("mdp register memory map failed: %d\n", rc); - sde_kms->mmio = NULL; + sde_kms->mmio = _sde_kms_ioremap(dev->platformdev, "mdp_phys", + &sde_kms->mmio_len); + if (!sde_kms->mmio) { + SDE_ERROR("mdp register memory map failed\n"); goto error; } DRM_INFO("mapped mdp address space @%p\n", sde_kms->mmio); - sde_kms->vbif[VBIF_RT] = msm_ioremap(dev->platformdev, - "vbif_phys", "VBIF"); - if (IS_ERR(sde_kms->vbif[VBIF_RT])) { - rc = PTR_ERR(sde_kms->vbif[VBIF_RT]); - SDE_ERROR("vbif register memory map failed: %d\n", rc); - sde_kms->vbif[VBIF_RT] = NULL; + rc = sde_dbg_reg_register_base(SDE_DBG_NAME, sde_kms->mmio, + sde_kms->mmio_len); + if (rc) + SDE_ERROR("dbg base register kms failed: %d\n", rc); + + sde_kms->vbif[VBIF_RT] = _sde_kms_ioremap(dev->platformdev, "vbif_phys", + &sde_kms->vbif_len[VBIF_RT]); + if (!sde_kms->vbif[VBIF_RT]) { + SDE_ERROR("vbif register memory map failed\n"); goto error; } - sde_kms->vbif[VBIF_NRT] = msm_ioremap(dev->platformdev, - "vbif_nrt_phys", "VBIF_NRT"); - if (IS_ERR(sde_kms->vbif[VBIF_NRT])) { - sde_kms->vbif[VBIF_NRT] = NULL; + rc = sde_dbg_reg_register_base("vbif_rt", sde_kms->vbif[VBIF_RT], + sde_kms->vbif_len[VBIF_RT]); + if (rc) + SDE_ERROR("dbg base register vbif_rt failed: %d\n", rc); + + sde_kms->vbif[VBIF_NRT] = _sde_kms_ioremap(dev->platformdev, + "vbif_nrt_phys", &sde_kms->vbif_len[VBIF_NRT]); + if (!sde_kms->vbif[VBIF_NRT]) { SDE_DEBUG("VBIF NRT is not defined"); + } else { + rc = sde_dbg_reg_register_base("vbif_nrt", + sde_kms->vbif[VBIF_NRT], + sde_kms->vbif_len[VBIF_NRT]); + if (rc) + SDE_ERROR("dbg base register vbif_nrt failed: %d\n", + rc); } sde_kms->core_client = sde_power_client_create(&priv->phandle, "core"); @@ -1213,6 +1296,8 @@ static int sde_kms_hw_init(struct msm_kms *kms) goto power_error; } + sde_dbg_init_dbg_buses(sde_kms->core_rev); + rc = sde_rm_init(&sde_kms->rm, sde_kms->catalog, sde_kms->mmio, sde_kms->dev); if (rc) { @@ -1230,6 +1315,35 @@ static int sde_kms_hw_init(struct msm_kms *kms) goto power_error; } + /* + * Read the DISP_INTF_SEL register to check + * whether early display is enabled in LK. + */ + rc = sde_splash_get_handoff_status(kms); + if (rc) { + SDE_ERROR("get early splash status failed: %d\n", rc); + goto power_error; + } + + /* + * when LK has enabled early display, sde_splash_parse_dt and + * sde_splash_init must be called. The first function is to parse the + * mandatory memory node for splash function, and the second function + * will first do bandwidth voting job, because display hardware is now + * accessing AHB data bus, otherwise device reboot will happen, and then + * to check if the memory is reserved. + */ + sinfo = &sde_kms->splash_info; + if (sinfo->handoff) { + rc = sde_splash_parse_dt(dev); + if (rc) { + SDE_ERROR("parse dt for splash info failed: %d\n", rc); + goto power_error; + } + + sde_splash_init(&priv->phandle, kms); + } + for (i = 0; i < sde_kms->catalog->vbif_count; i++) { u32 vbif_idx = sde_kms->catalog->vbif[i].id; @@ -1304,7 +1418,10 @@ static int sde_kms_hw_init(struct msm_kms *kms) */ dev->mode_config.allow_fb_modifiers = true; - sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false); + if (!sde_kms->splash_info.handoff) + sde_power_resource_enable(&priv->phandle, + sde_kms->core_client, false); + return 0; drm_obj_init_err: diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h index 44f6be959ac9..88ec4753a8a7 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.h +++ b/drivers/gpu/drm/msm/sde/sde_kms.h @@ -34,6 +34,7 @@ #include "sde_power_handle.h" #include "sde_irq.h" #include "sde_core_perf.h" +#include "sde_splash.h" #define DRMID(x) ((x) ? (x)->base.id : -1) @@ -133,6 +134,7 @@ struct sde_kms { /* io/register spaces: */ void __iomem *mmio, *vbif[VBIF_MAX]; + unsigned long mmio_len, vbif_len[VBIF_MAX]; struct regulator *vdd; struct regulator *mmagic; @@ -157,6 +159,9 @@ struct sde_kms { bool has_danger_ctrl; void **hdmi_displays; int hdmi_display_count; + + /* splash handoff structure */ + struct sde_splash_info splash_info; }; struct vsync_info { @@ -364,6 +369,49 @@ void sde_kms_info_append_format(struct sde_kms_info *info, void sde_kms_info_stop(struct sde_kms_info *info); /** + * sde_kms_rect_intersect - intersect two rectangles + * @r1: first rectangle + * @r2: scissor rectangle + * @result: result rectangle, all 0's on no intersection found + */ +void sde_kms_rect_intersect(const struct sde_rect *r1, + const struct sde_rect *r2, + struct sde_rect *result); + +/** + * sde_kms_rect_is_equal - compares two rects + * @r1: rect value to compare + * @r2: rect value to compare + * + * Returns 1 if the rects are same, 0 otherwise. + */ +static inline bool sde_kms_rect_is_equal(struct sde_rect *r1, + struct sde_rect *r2) +{ + if ((!r1 && r2) || (r1 && !r2)) + return false; + + if (!r1 && !r2) + return true; + + return r1->x == r2->x && r1->y == r2->y && r1->w == r2->w && + r1->h == r2->h; +} + +/** + * sde_kms_rect_is_null - returns true if the width or height of a rect is 0 + * @rect: rectangle to check for zero size + * @Return: True if width or height of rectangle is 0 + */ +static inline bool sde_kms_rect_is_null(const struct sde_rect *r) +{ + if (!r) + return true; + + return (!r->w || !r->h); +} + +/** * Vblank enable/disable functions */ int sde_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/msm/sde/sde_kms_utils.c b/drivers/gpu/drm/msm/sde/sde_kms_utils.c index 6e29c09deb40..30e12c969538 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms_utils.c +++ b/drivers/gpu/drm/msm/sde/sde_kms_utils.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -151,3 +151,27 @@ void sde_kms_info_stop(struct sde_kms_info *info) info->len = info->staged_len + len; } } + +void sde_kms_rect_intersect(const struct sde_rect *r1, + const struct sde_rect *r2, + struct sde_rect *result) +{ + int l, t, r, b; + + if (!r1 || !r2 || !result) + return; + + l = max(r1->x, r2->x); + t = max(r1->y, r2->y); + r = min((r1->x + r1->w), (r2->x + r2->w)); + b = min((r1->y + r1->h), (r2->y + r2->h)); + + if (r < l || b < t) { + memset(result, 0, sizeof(*result)); + } else { + result->x = l; + result->y = t; + result->w = r - l; + result->h = b - t; + } +} diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c index 6fe1d1629d22..6e2ccfa8e428 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.c +++ b/drivers/gpu/drm/msm/sde/sde_plane.c @@ -34,6 +34,14 @@ #include "sde_plane.h" #include "sde_color_processing.h" +static bool suspend_blank = true; +module_param(suspend_blank, bool, 0400); +MODULE_PARM_DESC(suspend_blank, + "If set, active planes will force their outputs to black,\n" + "by temporarily enabling the color fill, when recovering\n" + "from a system resume instead of attempting to display the\n" + "last provided frame buffer."); + #define SDE_DEBUG_PLANE(pl, fmt, ...) SDE_DEBUG("plane%d " fmt,\ (pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__) @@ -138,6 +146,7 @@ struct sde_plane { struct sde_debugfs_regset32 debugfs_src; struct sde_debugfs_regset32 debugfs_scaler; struct sde_debugfs_regset32 debugfs_csc; + bool debugfs_default_scale; }; #define to_sde_plane(x) container_of(x, struct sde_plane, base) @@ -686,9 +695,20 @@ static int _sde_plane_setup_scaler3_lut(struct sde_phy_plane *pp, struct sde_plane_state *pstate) { struct sde_plane *psde = pp->sde_plane; - struct sde_hw_scaler3_cfg *cfg = pp->scaler3_cfg; + struct sde_hw_scaler3_cfg *cfg; int ret = 0; + if (!pp || !pp->scaler3_cfg) { + SDE_ERROR("invalid args\n"); + return -EINVAL; + } else if (!pstate) { + /* pstate is expected to be null on forced color fill */ + SDE_DEBUG("null pstate\n"); + return -EINVAL; + } + + cfg = pp->scaler3_cfg; + cfg->dir_lut = msm_property_get_blob( &psde->property_info, pstate->property_blobs, &cfg->dir_len, @@ -723,6 +743,7 @@ static void _sde_plane_setup_scaler3(struct sde_phy_plane *pp, } memset(scale_cfg, 0, sizeof(*scale_cfg)); + memset(&pp->pixel_ext, 0, sizeof(struct sde_hw_pixel_ext)); decimated = DECIMATED_DIMENSION(src_w, pp->pipe_cfg.horz_decimation); @@ -1070,7 +1091,8 @@ static void _sde_plane_setup_scaler(struct sde_phy_plane *pp, int error; error = _sde_plane_setup_scaler3_lut(pp, pstate); - if (error || !pp->pixel_ext_usr) { + if (error || !pp->pixel_ext_usr || + psde->debugfs_default_scale) { memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); /* calculate default config for QSEED3 */ _sde_plane_setup_scaler3(pp, @@ -1081,7 +1103,8 @@ static void _sde_plane_setup_scaler(struct sde_phy_plane *pp, pp->scaler3_cfg, fmt, chroma_subsmpl_h, chroma_subsmpl_v); } - } else if (!pp->pixel_ext_usr) { + } else if (!pp->pixel_ext_usr || !pstate || + psde->debugfs_default_scale) { uint32_t deci_dim, i; /* calculate default configuration for QSEED2 */ @@ -1701,8 +1724,8 @@ void sde_plane_flush(struct drm_plane *plane) */ list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { if (psde->is_error) - /* force white frame with 0% alpha pipe output on error */ - _sde_plane_color_fill(pp, 0xFFFFFF, 0x0); + /* force white frame with 100% alpha pipe output on error */ + _sde_plane_color_fill(pp, 0xFFFFFF, 0xFF); else if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG) /* force 100% alpha */ _sde_plane_color_fill(pp, pp->color_fill, 0xFF); @@ -1711,6 +1734,10 @@ void sde_plane_flush(struct drm_plane *plane) pp->pipe_hw->ops.setup_csc(pp->pipe_hw, pp->csc_ptr); } + /* force black color fill during suspend */ + if (msm_is_suspend_state(plane->dev) && suspend_blank) + _sde_plane_color_fill(pp, 0x0, 0x0); + /* flag h/w flush complete */ if (plane->state) to_sde_plane_state(plane->state)->pending = false; @@ -2582,6 +2609,10 @@ static void _sde_plane_init_debugfs(struct sde_plane *psde, sde_debugfs_create_regset32("scaler_blk", S_IRUGO, psde->debugfs_root, &psde->debugfs_scaler); + debugfs_create_bool("default_scaling", + 0644, + psde->debugfs_root, + &psde->debugfs_default_scale); sde_debugfs_setup_regset32(&psde->debugfs_csc, sblk->csc_blk.base + cfg->base, diff --git a/drivers/gpu/drm/msm/sde/sde_splash.c b/drivers/gpu/drm/msm/sde/sde_splash.c new file mode 100644 index 000000000000..19e6406600cd --- /dev/null +++ b/drivers/gpu/drm/msm/sde/sde_splash.c @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/of_address.h> +#include <linux/debugfs.h> +#include <linux/memblock.h> + +#include "msm_drv.h" +#include "msm_mmu.h" +#include "sde_kms.h" +#include "sde_hw_mdss.h" +#include "sde_hw_util.h" +#include "sde_hw_intf.h" +#include "sde_hw_catalog.h" +#include "dsi_display.h" + +#define MDP_SSPP_TOP0_OFF 0x1000 +#define DISP_INTF_SEL 0x004 +#define SPLIT_DISPLAY_EN 0x2F4 + +/* scratch registers */ +#define SCRATCH_REGISTER_0 0x014 +#define SCRATCH_REGISTER_1 0x018 +#define SCRATCH_REGISTER_2 0x01C + +#define SDE_LK_RUNNING_VALUE 0xC001CAFE +#define SDE_LK_SHUT_DOWN_VALUE 0xDEADDEAD +#define SDE_LK_EXIT_VALUE 0xDEADBEEF + +#define SDE_LK_EXIT_MAX_LOOP 20 + +static DEFINE_MUTEX(sde_splash_lock); + +/* + * In order to free reseved memory from bootup, and we are not + * able to call the __init free functions, so we need to free + * this memory by ourselves using the free_reserved_page() function. + */ +static void _sde_splash_free_bootup_memory_to_system(phys_addr_t phys, + size_t size) +{ + unsigned long pfn_start, pfn_end, pfn_idx; + + memblock_free(phys, size); + + pfn_start = phys >> PAGE_SHIFT; + pfn_end = (phys + size) >> PAGE_SHIFT; + + for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++) + free_reserved_page(pfn_to_page(pfn_idx)); +} + +static int _sde_splash_parse_dt_get_lk_pool_node(struct drm_device *dev, + struct sde_splash_info *sinfo) +{ + struct device_node *parent, *node; + struct resource r; + int ret = 0; + + if (!sinfo) + return -EINVAL; + + parent = of_find_node_by_path("/reserved-memory"); + if (!parent) + return -EINVAL; + + node = of_find_node_by_name(parent, "lk_pool"); + if (!node) { + SDE_ERROR("mem reservation for lk_pool is not presented\n"); + ret = -EINVAL; + goto parent_node_err; + } + + /* find the mode */ + if (of_address_to_resource(node, 0, &r)) { + ret = -EINVAL; + goto child_node_err; + } + + sinfo->lk_pool_paddr = (dma_addr_t)r.start; + sinfo->lk_pool_size = r.end - r.start; + + DRM_INFO("lk_pool: addr:%pK, size:%pK\n", + (void *)sinfo->lk_pool_paddr, + (void *)sinfo->lk_pool_size); + +child_node_err: + of_node_put(node); + +parent_node_err: + of_node_put(parent); + + return ret; +} + +static int _sde_splash_parse_dt_get_display_node(struct drm_device *dev, + struct sde_splash_info *sinfo) +{ + unsigned long size = 0; + dma_addr_t start; + struct device_node *node; + int ret = 0, i = 0, len = 0; + + /* get reserved memory for display module */ + if (of_get_property(dev->dev->of_node, "contiguous-region", &len)) + sinfo->splash_mem_num = len / sizeof(u32); + else + sinfo->splash_mem_num = 0; + + sinfo->splash_mem_paddr = + kmalloc(sizeof(phys_addr_t) * sinfo->splash_mem_num, + GFP_KERNEL); + if (!sinfo->splash_mem_paddr) { + SDE_ERROR("alloc splash_mem_paddr failed\n"); + return -ENOMEM; + } + + sinfo->splash_mem_size = + kmalloc(sizeof(size_t) * sinfo->splash_mem_num, + GFP_KERNEL); + if (!sinfo->splash_mem_size) { + SDE_ERROR("alloc splash_mem_size failed\n"); + goto error; + } + + sinfo->obj = kmalloc(sizeof(struct drm_gem_object *) * + sinfo->splash_mem_num, GFP_KERNEL); + if (!sinfo->obj) { + SDE_ERROR("construct splash gem objects failed\n"); + goto error; + } + + for (i = 0; i < sinfo->splash_mem_num; i++) { + node = of_parse_phandle(dev->dev->of_node, + "contiguous-region", i); + + if (node) { + struct resource r; + + ret = of_address_to_resource(node, 0, &r); + if (ret) + return ret; + + size = r.end - r.start; + start = (dma_addr_t)r.start; + + sinfo->splash_mem_paddr[i] = start; + sinfo->splash_mem_size[i] = size; + + DRM_INFO("blk: %d, addr:%pK, size:%pK\n", + i, (void *)sinfo->splash_mem_paddr[i], + (void *)sinfo->splash_mem_size[i]); + + of_node_put(node); + } + } + + return ret; + +error: + kfree(sinfo->splash_mem_paddr); + sinfo->splash_mem_paddr = NULL; + + kfree(sinfo->splash_mem_size); + sinfo->splash_mem_size = NULL; + + return -ENOMEM; +} + +static bool _sde_splash_lk_check(struct sde_hw_intr *intr) +{ + return (SDE_LK_RUNNING_VALUE == SDE_REG_READ(&intr->hw, + SCRATCH_REGISTER_1)) ? true : false; +} + +/** + * _sde_splash_notify_lk_to_exit. + * + * Function to monitor LK's status and tell it to exit. + */ +static void _sde_splash_notify_lk_exit(struct sde_hw_intr *intr) +{ + int i = 0; + + /* first is to write exit signal to scratch register*/ + SDE_REG_WRITE(&intr->hw, SCRATCH_REGISTER_1, SDE_LK_SHUT_DOWN_VALUE); + + while ((SDE_LK_EXIT_VALUE != + SDE_REG_READ(&intr->hw, SCRATCH_REGISTER_1)) && + (++i < SDE_LK_EXIT_MAX_LOOP)) { + DRM_INFO("wait for LK's exit"); + msleep(20); + } + + if (i == SDE_LK_EXIT_MAX_LOOP) + SDE_ERROR("Loop LK's exit failed\n"); +} + +static int _sde_splash_gem_new(struct drm_device *dev, + struct sde_splash_info *sinfo) +{ + int i, ret; + + for (i = 0; i < sinfo->splash_mem_num; i++) { + sinfo->obj[i] = msm_gem_new(dev, + sinfo->splash_mem_size[i], MSM_BO_UNCACHED); + + if (IS_ERR(sinfo->obj[i])) { + ret = PTR_ERR(sinfo->obj[i]); + SDE_ERROR("failed to allocate gem, ret=%d\n", ret); + goto error; + } + } + + return 0; + +error: + for (i = 0; i < sinfo->splash_mem_num; i++) { + if (sinfo->obj[i]) + msm_gem_free_object(sinfo->obj[i]); + sinfo->obj[i] = NULL; + } + + return ret; +} + +static int _sde_splash_get_pages(struct drm_gem_object *obj, phys_addr_t phys) +{ + struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct page **p; + dma_addr_t paddr; + int npages = obj->size >> PAGE_SHIFT; + int i; + + p = drm_malloc_ab(npages, sizeof(struct page *)); + if (!p) + return -ENOMEM; + + paddr = phys; + + for (i = 0; i < npages; i++) { + p[i] = phys_to_page(paddr); + paddr += PAGE_SIZE; + } + + msm_obj->sgt = drm_prime_pages_to_sg(p, npages); + if (IS_ERR(msm_obj->sgt)) { + SDE_ERROR("failed to allocate sgt\n"); + return -ENOMEM; + } + + msm_obj->pages = p; + + return 0; +} + +static void _sde_splash_destroy_gem_object(struct msm_gem_object *msm_obj) +{ + if (msm_obj->pages) { + sg_free_table(msm_obj->sgt); + kfree(msm_obj->sgt); + drm_free_large(msm_obj->pages); + msm_obj->pages = NULL; + } +} + +static void _sde_splash_destroy_splash_node(struct sde_splash_info *sinfo) +{ + kfree(sinfo->splash_mem_paddr); + sinfo->splash_mem_paddr = NULL; + + kfree(sinfo->splash_mem_size); + sinfo->splash_mem_size = NULL; +} + +static void _sde_splash_get_connector_ref_cnt(struct sde_splash_info *sinfo, + u32 *hdmi_cnt, u32 *dsi_cnt) +{ + mutex_lock(&sde_splash_lock); + *hdmi_cnt = sinfo->hdmi_connector_cnt; + *dsi_cnt = sinfo->dsi_connector_cnt; + mutex_unlock(&sde_splash_lock); +} + +static int _sde_splash_free_resource(struct msm_mmu *mmu, + struct sde_splash_info *sinfo, enum splash_connector_type conn) +{ + struct msm_gem_object *msm_obj = to_msm_bo(sinfo->obj[conn]); + + if (!msm_obj) + return -EINVAL; + + if (mmu->funcs && mmu->funcs->unmap) + mmu->funcs->unmap(mmu, sinfo->splash_mem_paddr[conn], + msm_obj->sgt, NULL); + + _sde_splash_free_bootup_memory_to_system(sinfo->splash_mem_paddr[conn], + sinfo->splash_mem_size[conn]); + + _sde_splash_destroy_gem_object(msm_obj); + + return 0; +} + +__ref int sde_splash_init(struct sde_power_handle *phandle, struct msm_kms *kms) +{ + struct sde_kms *sde_kms; + struct sde_splash_info *sinfo; + int i = 0; + + if (!phandle || !kms) { + SDE_ERROR("invalid phandle/kms\n"); + return -EINVAL; + } + + sde_kms = to_sde_kms(kms); + sinfo = &sde_kms->splash_info; + + sinfo->dsi_connector_cnt = 0; + sinfo->hdmi_connector_cnt = 0; + + sde_power_data_bus_bandwidth_ctrl(phandle, + sde_kms->core_client, true); + + for (i = 0; i < sinfo->splash_mem_num; i++) { + if (!memblock_is_reserved(sinfo->splash_mem_paddr[i])) { + SDE_ERROR("failed to reserve memory\n"); + + /* withdraw the vote when failed. */ + sde_power_data_bus_bandwidth_ctrl(phandle, + sde_kms->core_client, false); + + return -EINVAL; + } + } + + return 0; +} + +void sde_splash_destroy(struct sde_splash_info *sinfo, + struct sde_power_handle *phandle, + struct sde_power_client *pclient) +{ + struct msm_gem_object *msm_obj; + int i = 0; + + if (!sinfo || !phandle || !pclient) { + SDE_ERROR("invalid sde_kms/phandle/pclient\n"); + return; + } + + for (i = 0; i < sinfo->splash_mem_num; i++) { + msm_obj = to_msm_bo(sinfo->obj[i]); + + if (msm_obj) + _sde_splash_destroy_gem_object(msm_obj); + } + + sde_power_data_bus_bandwidth_ctrl(phandle, pclient, false); + + _sde_splash_destroy_splash_node(sinfo); +} + +/* + * sde_splash_parse_dt. + * In the function, it will parse and reserve two kinds of memory node. + * First is to get the reserved memory for display buffers. + * Second is to get the memory node LK's code stack is running on. + */ +int sde_splash_parse_dt(struct drm_device *dev) +{ + struct msm_drm_private *priv = dev->dev_private; + struct sde_kms *sde_kms; + struct sde_splash_info *sinfo; + + if (!priv || !priv->kms) { + SDE_ERROR("Invalid kms\n"); + return -EINVAL; + } + + sde_kms = to_sde_kms(priv->kms); + sinfo = &sde_kms->splash_info; + + if (_sde_splash_parse_dt_get_display_node(dev, sinfo)) { + SDE_ERROR("get display node failed\n"); + return -EINVAL; + } + + if (_sde_splash_parse_dt_get_lk_pool_node(dev, sinfo)) { + SDE_ERROR("get LK pool node failed\n"); + return -EINVAL; + } + + return 0; +} + +int sde_splash_get_handoff_status(struct msm_kms *kms) +{ + uint32_t intf_sel = 0; + uint32_t split_display = 0; + uint32_t num_of_display_on = 0; + uint32_t i = 0; + struct sde_kms *sde_kms = to_sde_kms(kms); + struct sde_rm *rm; + struct sde_hw_blk_reg_map *c; + struct sde_splash_info *sinfo; + struct sde_mdss_cfg *catalog; + + sinfo = &sde_kms->splash_info; + if (!sinfo) { + SDE_ERROR("%s(%d): invalid splash info\n", + __func__, __LINE__); + return -EINVAL; + } + + rm = &sde_kms->rm; + + if (!rm || !rm->hw_mdp) { + SDE_ERROR("invalid rm.\n"); + return -EINVAL; + } + + c = &rm->hw_mdp->hw; + if (c) { + intf_sel = SDE_REG_READ(c, DISP_INTF_SEL); + split_display = SDE_REG_READ(c, SPLIT_DISPLAY_EN); + } + + catalog = sde_kms->catalog; + + if (intf_sel != 0) { + for (i = 0; i < catalog->intf_count; i++) + if ((intf_sel >> i*8) & 0x000000FF) + num_of_display_on++; + + /* + * For split display enabled - DSI0, DSI1 interfaces are + * considered as single display. So decrement + * 'num_of_display_on' by 1 + */ + if (split_display) + num_of_display_on--; + } + + if (num_of_display_on) { + sinfo->handoff = true; + sinfo->program_scratch_regs = true; + sinfo->lk_is_exited = false; + } else { + sinfo->handoff = false; + sinfo->program_scratch_regs = false; + sinfo->lk_is_exited = true; + } + + return 0; +} + +int sde_splash_smmu_map(struct drm_device *dev, struct msm_mmu *mmu, + struct sde_splash_info *sinfo) +{ + struct msm_gem_object *msm_obj; + int i = 0, ret = 0; + + if (!mmu || !sinfo) + return -EINVAL; + + /* first is to construct drm_gem_objects for splash memory */ + if (_sde_splash_gem_new(dev, sinfo)) + return -ENOMEM; + + /* second is to contruct sgt table for calling smmu map */ + for (i = 0; i < sinfo->splash_mem_num; i++) { + if (_sde_splash_get_pages(sinfo->obj[i], + sinfo->splash_mem_paddr[i])) + return -ENOMEM; + } + + for (i = 0; i < sinfo->splash_mem_num; i++) { + msm_obj = to_msm_bo(sinfo->obj[i]); + + if (mmu->funcs && mmu->funcs->map) { + ret = mmu->funcs->map(mmu, sinfo->splash_mem_paddr[i], + msm_obj->sgt, IOMMU_READ | IOMMU_NOEXEC, NULL); + + if (!ret) { + SDE_ERROR("Map blk %d @%pK failed.\n", + i, (void *)sinfo->splash_mem_paddr[i]); + return ret; + } + } + } + + return ret ? 0 : -ENOMEM; +} + +void sde_splash_setup_connector_count(struct sde_splash_info *sinfo, + int connector_type) +{ + switch (connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + sinfo->hdmi_connector_cnt++; + break; + case DRM_MODE_CONNECTOR_DSI: + sinfo->dsi_connector_cnt++; + break; + default: + SDE_ERROR("invalid connector_type %d\n", connector_type); + } +} + +bool sde_splash_get_lk_complete_status(struct sde_splash_info *sinfo) +{ + bool ret = 0; + + mutex_lock(&sde_splash_lock); + ret = !sinfo->handoff && !sinfo->lk_is_exited; + mutex_unlock(&sde_splash_lock); + + return ret; +} + +int sde_splash_clean_up_free_resource(struct msm_kms *kms, + struct sde_power_handle *phandle, + int connector_type, void *display) +{ + struct sde_kms *sde_kms; + struct sde_splash_info *sinfo; + struct msm_mmu *mmu; + struct dsi_display *dsi_display = display; + int ret = 0; + int hdmi_conn_count = 0; + int dsi_conn_count = 0; + static const char *last_commit_display_type = "unknown"; + + if (!phandle || !kms) { + SDE_ERROR("invalid phandle/kms.\n"); + return -EINVAL; + } + + sde_kms = to_sde_kms(kms); + sinfo = &sde_kms->splash_info; + if (!sinfo) { + SDE_ERROR("%s(%d): invalid splash info\n", __func__, __LINE__); + return -EINVAL; + } + + _sde_splash_get_connector_ref_cnt(sinfo, &hdmi_conn_count, + &dsi_conn_count); + + mutex_lock(&sde_splash_lock); + if (hdmi_conn_count == 0 && dsi_conn_count == 0 && + !sinfo->lk_is_exited) { + /* When both hdmi's and dsi's handoff are finished, + * 1. Destroy splash node objects. + * 2. Release the memory which LK's stack is running on. + * 3. Withdraw AHB data bus bandwidth voting. + */ + DRM_INFO("HDMI and DSI resource handoff is completed\n"); + + sinfo->lk_is_exited = true; + + _sde_splash_destroy_splash_node(sinfo); + + _sde_splash_free_bootup_memory_to_system(sinfo->lk_pool_paddr, + sinfo->lk_pool_size); + + sde_power_data_bus_bandwidth_ctrl(phandle, + sde_kms->core_client, false); + + mutex_unlock(&sde_splash_lock); + return 0; + } + + mmu = sde_kms->aspace[0]->mmu; + + switch (connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + if (sinfo->hdmi_connector_cnt == 1) { + sinfo->hdmi_connector_cnt--; + + ret = _sde_splash_free_resource(mmu, + sinfo, SPLASH_HDMI); + } + break; + case DRM_MODE_CONNECTOR_DSI: + /* + * Basically, we have commits coming on two DSI connectors. + * So when releasing DSI resource, it's ensured that the + * coming commits should happen on different DSIs, to promise + * the handoff has finished on the two DSIs, then it's safe + * to release DSI resource, otherwise, problem happens when + * freeing memory, while DSI0 or DSI1 is still visiting + * the memory. + */ + if (strcmp(dsi_display->display_type, "unknown") && + strcmp(last_commit_display_type, + dsi_display->display_type)) { + if (sinfo->dsi_connector_cnt > 1) + sinfo->dsi_connector_cnt--; + else if (sinfo->dsi_connector_cnt == 1) { + ret = _sde_splash_free_resource(mmu, + sinfo, SPLASH_DSI); + + sinfo->dsi_connector_cnt--; + } + + last_commit_display_type = dsi_display->display_type; + } + break; + default: + ret = -EINVAL; + SDE_ERROR("%s: invalid connector_type %d\n", + __func__, connector_type); + } + + mutex_unlock(&sde_splash_lock); + + return ret; +} + +/* + * In below function, it will + * 1. Notify LK to exit and wait for exiting is done. + * 2. Set DOMAIN_ATTR_EARLY_MAP to 1 to enable stage 1 translation in iommu. + */ +int sde_splash_clean_up_exit_lk(struct msm_kms *kms) +{ + struct sde_splash_info *sinfo; + struct msm_mmu *mmu; + struct sde_kms *sde_kms = to_sde_kms(kms); + int ret; + + sinfo = &sde_kms->splash_info; + + if (!sinfo) { + SDE_ERROR("%s(%d): invalid splash info\n", __func__, __LINE__); + return -EINVAL; + } + + /* Monitor LK's status and tell it to exit. */ + mutex_lock(&sde_splash_lock); + if (sinfo->program_scratch_regs) { + if (_sde_splash_lk_check(sde_kms->hw_intr)) + _sde_splash_notify_lk_exit(sde_kms->hw_intr); + + sinfo->handoff = false; + sinfo->program_scratch_regs = false; + } + mutex_unlock(&sde_splash_lock); + + if (!sde_kms->aspace[0] || !sde_kms->aspace[0]->mmu) { + /* We do not return fault value here, to ensure + * flag "lk_is_exited" is set. + */ + SDE_ERROR("invalid mmu\n"); + WARN_ON(1); + } else { + mmu = sde_kms->aspace[0]->mmu; + /* After LK has exited, set early domain map attribute + * to 1 to enable stage 1 translation in iommu driver. + */ + if (mmu->funcs && mmu->funcs->set_property) { + ret = mmu->funcs->set_property(mmu, + DOMAIN_ATTR_EARLY_MAP, &sinfo->handoff); + + if (ret) + SDE_ERROR("set_property failed\n"); + } + } + + return 0; +} diff --git a/drivers/gpu/drm/msm/sde/sde_splash.h b/drivers/gpu/drm/msm/sde/sde_splash.h new file mode 100644 index 000000000000..babf88335e49 --- /dev/null +++ b/drivers/gpu/drm/msm/sde/sde_splash.h @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef SDE_SPLASH_H_ +#define SDE_SPLASH_H_ + +#include "msm_kms.h" +#include "msm_mmu.h" + +enum splash_connector_type { + SPLASH_DSI = 0, + SPLASH_HDMI, +}; + +struct sde_splash_info { + /* handoff flag */ + bool handoff; + + /* flag of display scratch registers */ + bool program_scratch_regs; + + /* to indicate LK is totally exited */ + bool lk_is_exited; + + /* memory node used for display buffer */ + uint32_t splash_mem_num; + + /* physical address of memory node for display buffer */ + phys_addr_t *splash_mem_paddr; + + /* size of memory node */ + size_t *splash_mem_size; + + /* constructed gem objects for smmu mapping */ + struct drm_gem_object **obj; + + /* physical address of lk pool */ + phys_addr_t lk_pool_paddr; + + /* memory size of lk pool */ + size_t lk_pool_size; + + /* registered hdmi connector count */ + uint32_t hdmi_connector_cnt; + + /* registered dst connector count */ + uint32_t dsi_connector_cnt; +}; + +/* APIs for early splash handoff functions */ + +/** + * sde_splash_get_handoff_status. + * + * This function will read DISP_INTF_SEL regsiter to get + * the status of early splash. + */ +int sde_splash_get_handoff_status(struct msm_kms *kms); + +/** + * sde_splash_init + * + * This function will do bandwidth vote and reserved memory + */ +int sde_splash_init(struct sde_power_handle *phandle, struct msm_kms *kms); + +/** + *sde_splash_setup_connector_count + * + * To count connector numbers for DSI and HDMI respectively. + */ +void sde_splash_setup_connector_count(struct sde_splash_info *sinfo, + int connector_type); + +/** + * sde_splash_clean_up_exit_lk. + * + * Tell LK to exit, and clean up the resource. + */ +int sde_splash_clean_up_exit_lk(struct msm_kms *kms); + +/** + * sde_splash_clean_up_free_resource. + * + * According to input connector_type, free + * HDMI's and DSI's resource respectively. + */ +int sde_splash_clean_up_free_resource(struct msm_kms *kms, + struct sde_power_handle *phandle, + int connector_type, void *display); + +/** + * sde_splash_parse_dt. + * + * Parse reserved memory block from DT for early splash. + */ +int sde_splash_parse_dt(struct drm_device *dev); + +/** + * sde_splash_smmu_map. + * + * Map the physical memory LK visited into iommu driver. + */ +int sde_splash_smmu_map(struct drm_device *dev, struct msm_mmu *mmu, + struct sde_splash_info *sinfo); + +/** + * sde_splash_destroy + * + * Destroy the resource in failed case. + */ +void sde_splash_destroy(struct sde_splash_info *sinfo, + struct sde_power_handle *phandle, + struct sde_power_client *pclient); + +/** + * sde_splash_get_lk_complete_status + * + * Get LK's status to check if it has been stopped. + */ +bool sde_splash_get_lk_complete_status(struct sde_splash_info *sinfo); + +#endif diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c new file mode 100644 index 000000000000..b87bc75f17ea --- /dev/null +++ b/drivers/gpu/drm/msm/sde_dbg.c @@ -0,0 +1,2070 @@ +/* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ + +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/ktime.h> +#include <linux/debugfs.h> +#include <linux/uaccess.h> +#include <linux/dma-buf.h> +#include <linux/slab.h> +#include <linux/list_sort.h> + +#include "sde_dbg.h" +#include "sde/sde_hw_catalog.h" + +#define SDE_DBG_BASE_MAX 10 + +#define DEFAULT_PANIC 1 +#define DEFAULT_REGDUMP SDE_DBG_DUMP_IN_MEM +#define DEFAULT_DBGBUS_SDE SDE_DBG_DUMP_IN_MEM +#define DEFAULT_DBGBUS_VBIFRT SDE_DBG_DUMP_IN_MEM +#define DEFAULT_BASE_REG_CNT 0x100 +#define GROUP_BYTES 4 +#define ROW_BYTES 16 +#define RANGE_NAME_LEN 40 +#define REG_BASE_NAME_LEN 80 + +#define DBGBUS_FLAGS_DSPP BIT(0) +#define DBGBUS_DSPP_STATUS 0x34C + +#define DBGBUS_NAME_SDE "sde" +#define DBGBUS_NAME_VBIF_RT "vbif_rt" + +/* offsets from sde top address for the debug buses */ +#define DBGBUS_SSPP0 0x188 +#define DBGBUS_SSPP1 0x298 +#define DBGBUS_DSPP 0x348 +#define DBGBUS_PERIPH 0x418 + +#define TEST_MASK(id, tp) ((id << 4) | (tp << 1) | BIT(0)) + +/* following offsets are with respect to MDP VBIF base for DBG BUS access */ +#define MMSS_VBIF_CLKON 0x4 +#define MMSS_VBIF_TEST_BUS_OUT_CTRL 0x210 +#define MMSS_VBIF_TEST_BUS_OUT 0x230 + +/* print debug ranges in groups of 4 u32s */ +#define REG_DUMP_ALIGN 16 + +/** + * struct sde_dbg_reg_offset - tracking for start and end of region + * @start: start offset + * @start: end offset + */ +struct sde_dbg_reg_offset { + u32 start; + u32 end; +}; + +/** + * struct sde_dbg_reg_range - register dumping named sub-range + * @head: head of this node + * @reg_dump: address for the mem dump + * @range_name: name of this range + * @offset: offsets for range to dump + * @xin_id: client xin id + */ +struct sde_dbg_reg_range { + struct list_head head; + u32 *reg_dump; + char range_name[RANGE_NAME_LEN]; + struct sde_dbg_reg_offset offset; + uint32_t xin_id; +}; + +/** + * struct sde_dbg_reg_base - register region base. + * may sub-ranges: sub-ranges are used for dumping + * or may not have sub-ranges: dumping is base -> max_offset + * @reg_base_head: head of this node + * @sub_range_list: head to the list with dump ranges + * @name: register base name + * @base: base pointer + * @off: cached offset of region for manual register dumping + * @cnt: cached range of region for manual register dumping + * @max_offset: length of region + * @buf: buffer used for manual register dumping + * @buf_len: buffer length used for manual register dumping + * @reg_dump: address for the mem dump if no ranges used + */ +struct sde_dbg_reg_base { + struct list_head reg_base_head; + struct list_head sub_range_list; + char name[REG_BASE_NAME_LEN]; + void __iomem *base; + size_t off; + size_t cnt; + size_t max_offset; + char *buf; + size_t buf_len; + u32 *reg_dump; +}; + +struct sde_debug_bus_entry { + u32 wr_addr; + u32 block_id; + u32 test_id; +}; + +struct vbif_debug_bus_entry { + u32 disable_bus_addr; + u32 block_bus_addr; + u32 bit_offset; + u32 block_cnt; + u32 test_pnt_start; + u32 test_pnt_cnt; +}; + +struct sde_dbg_debug_bus_common { + char *name; + u32 enable_mask; + bool include_in_deferred_work; + u32 flags; + u32 entries_size; + u32 *dumped_content; +}; + +struct sde_dbg_sde_debug_bus { + struct sde_dbg_debug_bus_common cmn; + struct sde_debug_bus_entry *entries; + u32 top_blk_off; +}; + +struct sde_dbg_vbif_debug_bus { + struct sde_dbg_debug_bus_common cmn; + struct vbif_debug_bus_entry *entries; +}; + +/** + * struct sde_dbg_base - global sde debug base structure + * @evtlog: event log instance + * @reg_base_list: list of register dumping regions + * @root: base debugfs root + * @dev: device pointer + * @power_ctrl: callback structure for enabling power for reading hw registers + * @req_dump_blks: list of blocks requested for dumping + * @panic_on_err: whether to kernel panic after triggering dump via debugfs + * @dump_work: work struct for deferring register dump work to separate thread + * @work_panic: panic after dump if internal user passed "panic" special region + * @enable_reg_dump: whether to dump registers into memory, kernel log, or both + * @dbgbus_sde: debug bus structure for the sde + * @dbgbus_vbif_rt: debug bus structure for the realtime vbif + */ +static struct sde_dbg_base { + struct sde_dbg_evtlog *evtlog; + struct list_head reg_base_list; + struct dentry *root; + struct device *dev; + struct sde_dbg_power_ctrl power_ctrl; + + struct sde_dbg_reg_base *req_dump_blks[SDE_DBG_BASE_MAX]; + + u32 panic_on_err; + struct work_struct dump_work; + bool work_panic; + u32 enable_reg_dump; + + struct sde_dbg_sde_debug_bus dbgbus_sde; + struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt; +} sde_dbg_base; + +/* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */ +struct sde_dbg_evtlog *sde_dbg_base_evtlog; + +static struct sde_debug_bus_entry dbg_bus_sde_8998[] = { + + /* Unpack 0 sspp 0*/ + { DBGBUS_SSPP0, 50, 2 }, + { DBGBUS_SSPP0, 60, 2 }, + { DBGBUS_SSPP0, 70, 2 }, + { DBGBUS_SSPP0, 85, 2 }, + + /* Upack 0 sspp 1*/ + { DBGBUS_SSPP1, 50, 2 }, + { DBGBUS_SSPP1, 60, 2 }, + { DBGBUS_SSPP1, 70, 2 }, + { DBGBUS_SSPP1, 85, 2 }, + + /* scheduler */ + { DBGBUS_DSPP, 130, 0 }, + { DBGBUS_DSPP, 130, 1 }, + { DBGBUS_DSPP, 130, 2 }, + { DBGBUS_DSPP, 130, 3 }, + { DBGBUS_DSPP, 130, 4 }, + { DBGBUS_DSPP, 130, 5 }, + + /* qseed */ + { DBGBUS_SSPP0, 6, 0}, + { DBGBUS_SSPP0, 6, 1}, + { DBGBUS_SSPP0, 26, 0}, + { DBGBUS_SSPP0, 26, 1}, + { DBGBUS_SSPP1, 6, 0}, + { DBGBUS_SSPP1, 6, 1}, + { DBGBUS_SSPP1, 26, 0}, + { DBGBUS_SSPP1, 26, 1}, + + /* scale */ + { DBGBUS_SSPP0, 16, 0}, + { DBGBUS_SSPP0, 16, 1}, + { DBGBUS_SSPP0, 36, 0}, + { DBGBUS_SSPP0, 36, 1}, + { DBGBUS_SSPP1, 16, 0}, + { DBGBUS_SSPP1, 16, 1}, + { DBGBUS_SSPP1, 36, 0}, + { DBGBUS_SSPP1, 36, 1}, + + /* fetch sspp0 */ + + /* vig 0 */ + { DBGBUS_SSPP0, 0, 0 }, + { DBGBUS_SSPP0, 0, 1 }, + { DBGBUS_SSPP0, 0, 2 }, + { DBGBUS_SSPP0, 0, 3 }, + { DBGBUS_SSPP0, 0, 4 }, + { DBGBUS_SSPP0, 0, 5 }, + { DBGBUS_SSPP0, 0, 6 }, + { DBGBUS_SSPP0, 0, 7 }, + + { DBGBUS_SSPP0, 1, 0 }, + { DBGBUS_SSPP0, 1, 1 }, + { DBGBUS_SSPP0, 1, 2 }, + { DBGBUS_SSPP0, 1, 3 }, + { DBGBUS_SSPP0, 1, 4 }, + { DBGBUS_SSPP0, 1, 5 }, + { DBGBUS_SSPP0, 1, 6 }, + { DBGBUS_SSPP0, 1, 7 }, + + { DBGBUS_SSPP0, 2, 0 }, + { DBGBUS_SSPP0, 2, 1 }, + { DBGBUS_SSPP0, 2, 2 }, + { DBGBUS_SSPP0, 2, 3 }, + { DBGBUS_SSPP0, 2, 4 }, + { DBGBUS_SSPP0, 2, 5 }, + { DBGBUS_SSPP0, 2, 6 }, + { DBGBUS_SSPP0, 2, 7 }, + + { DBGBUS_SSPP0, 4, 0 }, + { DBGBUS_SSPP0, 4, 1 }, + { DBGBUS_SSPP0, 4, 2 }, + { DBGBUS_SSPP0, 4, 3 }, + { DBGBUS_SSPP0, 4, 4 }, + { DBGBUS_SSPP0, 4, 5 }, + { DBGBUS_SSPP0, 4, 6 }, + { DBGBUS_SSPP0, 4, 7 }, + + { DBGBUS_SSPP0, 5, 0 }, + { DBGBUS_SSPP0, 5, 1 }, + { DBGBUS_SSPP0, 5, 2 }, + { DBGBUS_SSPP0, 5, 3 }, + { DBGBUS_SSPP0, 5, 4 }, + { DBGBUS_SSPP0, 5, 5 }, + { DBGBUS_SSPP0, 5, 6 }, + { DBGBUS_SSPP0, 5, 7 }, + + /* vig 2 */ + { DBGBUS_SSPP0, 20, 0 }, + { DBGBUS_SSPP0, 20, 1 }, + { DBGBUS_SSPP0, 20, 2 }, + { DBGBUS_SSPP0, 20, 3 }, + { DBGBUS_SSPP0, 20, 4 }, + { DBGBUS_SSPP0, 20, 5 }, + { DBGBUS_SSPP0, 20, 6 }, + { DBGBUS_SSPP0, 20, 7 }, + + { DBGBUS_SSPP0, 21, 0 }, + { DBGBUS_SSPP0, 21, 1 }, + { DBGBUS_SSPP0, 21, 2 }, + { DBGBUS_SSPP0, 21, 3 }, + { DBGBUS_SSPP0, 21, 4 }, + { DBGBUS_SSPP0, 21, 5 }, + { DBGBUS_SSPP0, 21, 6 }, + { DBGBUS_SSPP0, 21, 7 }, + + { DBGBUS_SSPP0, 22, 0 }, + { DBGBUS_SSPP0, 22, 1 }, + { DBGBUS_SSPP0, 22, 2 }, + { DBGBUS_SSPP0, 22, 3 }, + { DBGBUS_SSPP0, 22, 4 }, + { DBGBUS_SSPP0, 22, 5 }, + { DBGBUS_SSPP0, 22, 6 }, + { DBGBUS_SSPP0, 22, 7 }, + + { DBGBUS_SSPP0, 24, 0 }, + { DBGBUS_SSPP0, 24, 1 }, + { DBGBUS_SSPP0, 24, 2 }, + { DBGBUS_SSPP0, 24, 3 }, + { DBGBUS_SSPP0, 24, 4 }, + { DBGBUS_SSPP0, 24, 5 }, + { DBGBUS_SSPP0, 24, 6 }, + { DBGBUS_SSPP0, 24, 7 }, + + { DBGBUS_SSPP0, 25, 0 }, + { DBGBUS_SSPP0, 25, 1 }, + { DBGBUS_SSPP0, 25, 2 }, + { DBGBUS_SSPP0, 25, 3 }, + { DBGBUS_SSPP0, 25, 4 }, + { DBGBUS_SSPP0, 25, 5 }, + { DBGBUS_SSPP0, 25, 6 }, + { DBGBUS_SSPP0, 25, 7 }, + + /* dma 2 */ + { DBGBUS_SSPP0, 30, 0 }, + { DBGBUS_SSPP0, 30, 1 }, + { DBGBUS_SSPP0, 30, 2 }, + { DBGBUS_SSPP0, 30, 3 }, + { DBGBUS_SSPP0, 30, 4 }, + { DBGBUS_SSPP0, 30, 5 }, + { DBGBUS_SSPP0, 30, 6 }, + { DBGBUS_SSPP0, 30, 7 }, + + { DBGBUS_SSPP0, 31, 0 }, + { DBGBUS_SSPP0, 31, 1 }, + { DBGBUS_SSPP0, 31, 2 }, + { DBGBUS_SSPP0, 31, 3 }, + { DBGBUS_SSPP0, 31, 4 }, + { DBGBUS_SSPP0, 31, 5 }, + { DBGBUS_SSPP0, 31, 6 }, + { DBGBUS_SSPP0, 31, 7 }, + + { DBGBUS_SSPP0, 32, 0 }, + { DBGBUS_SSPP0, 32, 1 }, + { DBGBUS_SSPP0, 32, 2 }, + { DBGBUS_SSPP0, 32, 3 }, + { DBGBUS_SSPP0, 32, 4 }, + { DBGBUS_SSPP0, 32, 5 }, + { DBGBUS_SSPP0, 32, 6 }, + { DBGBUS_SSPP0, 32, 7 }, + + { DBGBUS_SSPP0, 33, 0 }, + { DBGBUS_SSPP0, 33, 1 }, + { DBGBUS_SSPP0, 33, 2 }, + { DBGBUS_SSPP0, 33, 3 }, + { DBGBUS_SSPP0, 33, 4 }, + { DBGBUS_SSPP0, 33, 5 }, + { DBGBUS_SSPP0, 33, 6 }, + { DBGBUS_SSPP0, 33, 7 }, + + { DBGBUS_SSPP0, 34, 0 }, + { DBGBUS_SSPP0, 34, 1 }, + { DBGBUS_SSPP0, 34, 2 }, + { DBGBUS_SSPP0, 34, 3 }, + { DBGBUS_SSPP0, 34, 4 }, + { DBGBUS_SSPP0, 34, 5 }, + { DBGBUS_SSPP0, 34, 6 }, + { DBGBUS_SSPP0, 34, 7 }, + + { DBGBUS_SSPP0, 35, 0 }, + { DBGBUS_SSPP0, 35, 1 }, + { DBGBUS_SSPP0, 35, 2 }, + { DBGBUS_SSPP0, 35, 3 }, + + /* dma 0 */ + { DBGBUS_SSPP0, 40, 0 }, + { DBGBUS_SSPP0, 40, 1 }, + { DBGBUS_SSPP0, 40, 2 }, + { DBGBUS_SSPP0, 40, 3 }, + { DBGBUS_SSPP0, 40, 4 }, + { DBGBUS_SSPP0, 40, 5 }, + { DBGBUS_SSPP0, 40, 6 }, + { DBGBUS_SSPP0, 40, 7 }, + + { DBGBUS_SSPP0, 41, 0 }, + { DBGBUS_SSPP0, 41, 1 }, + { DBGBUS_SSPP0, 41, 2 }, + { DBGBUS_SSPP0, 41, 3 }, + { DBGBUS_SSPP0, 41, 4 }, + { DBGBUS_SSPP0, 41, 5 }, + { DBGBUS_SSPP0, 41, 6 }, + { DBGBUS_SSPP0, 41, 7 }, + + { DBGBUS_SSPP0, 42, 0 }, + { DBGBUS_SSPP0, 42, 1 }, + { DBGBUS_SSPP0, 42, 2 }, + { DBGBUS_SSPP0, 42, 3 }, + { DBGBUS_SSPP0, 42, 4 }, + { DBGBUS_SSPP0, 42, 5 }, + { DBGBUS_SSPP0, 42, 6 }, + { DBGBUS_SSPP0, 42, 7 }, + + { DBGBUS_SSPP0, 44, 0 }, + { DBGBUS_SSPP0, 44, 1 }, + { DBGBUS_SSPP0, 44, 2 }, + { DBGBUS_SSPP0, 44, 3 }, + { DBGBUS_SSPP0, 44, 4 }, + { DBGBUS_SSPP0, 44, 5 }, + { DBGBUS_SSPP0, 44, 6 }, + { DBGBUS_SSPP0, 44, 7 }, + + { DBGBUS_SSPP0, 45, 0 }, + { DBGBUS_SSPP0, 45, 1 }, + { DBGBUS_SSPP0, 45, 2 }, + { DBGBUS_SSPP0, 45, 3 }, + { DBGBUS_SSPP0, 45, 4 }, + { DBGBUS_SSPP0, 45, 5 }, + { DBGBUS_SSPP0, 45, 6 }, + { DBGBUS_SSPP0, 45, 7 }, + + /* fetch sspp1 */ + /* vig 1 */ + { DBGBUS_SSPP1, 0, 0 }, + { DBGBUS_SSPP1, 0, 1 }, + { DBGBUS_SSPP1, 0, 2 }, + { DBGBUS_SSPP1, 0, 3 }, + { DBGBUS_SSPP1, 0, 4 }, + { DBGBUS_SSPP1, 0, 5 }, + { DBGBUS_SSPP1, 0, 6 }, + { DBGBUS_SSPP1, 0, 7 }, + + { DBGBUS_SSPP1, 1, 0 }, + { DBGBUS_SSPP1, 1, 1 }, + { DBGBUS_SSPP1, 1, 2 }, + { DBGBUS_SSPP1, 1, 3 }, + { DBGBUS_SSPP1, 1, 4 }, + { DBGBUS_SSPP1, 1, 5 }, + { DBGBUS_SSPP1, 1, 6 }, + { DBGBUS_SSPP1, 1, 7 }, + + { DBGBUS_SSPP1, 2, 0 }, + { DBGBUS_SSPP1, 2, 1 }, + { DBGBUS_SSPP1, 2, 2 }, + { DBGBUS_SSPP1, 2, 3 }, + { DBGBUS_SSPP1, 2, 4 }, + { DBGBUS_SSPP1, 2, 5 }, + { DBGBUS_SSPP1, 2, 6 }, + { DBGBUS_SSPP1, 2, 7 }, + + { DBGBUS_SSPP1, 4, 0 }, + { DBGBUS_SSPP1, 4, 1 }, + { DBGBUS_SSPP1, 4, 2 }, + { DBGBUS_SSPP1, 4, 3 }, + { DBGBUS_SSPP1, 4, 4 }, + { DBGBUS_SSPP1, 4, 5 }, + { DBGBUS_SSPP1, 4, 6 }, + { DBGBUS_SSPP1, 4, 7 }, + + { DBGBUS_SSPP1, 5, 0 }, + { DBGBUS_SSPP1, 5, 1 }, + { DBGBUS_SSPP1, 5, 2 }, + { DBGBUS_SSPP1, 5, 3 }, + { DBGBUS_SSPP1, 5, 4 }, + { DBGBUS_SSPP1, 5, 5 }, + { DBGBUS_SSPP1, 5, 6 }, + { DBGBUS_SSPP1, 5, 7 }, + + /* vig 3 */ + { DBGBUS_SSPP1, 20, 0 }, + { DBGBUS_SSPP1, 20, 1 }, + { DBGBUS_SSPP1, 20, 2 }, + { DBGBUS_SSPP1, 20, 3 }, + { DBGBUS_SSPP1, 20, 4 }, + { DBGBUS_SSPP1, 20, 5 }, + { DBGBUS_SSPP1, 20, 6 }, + { DBGBUS_SSPP1, 20, 7 }, + + { DBGBUS_SSPP1, 21, 0 }, + { DBGBUS_SSPP1, 21, 1 }, + { DBGBUS_SSPP1, 21, 2 }, + { DBGBUS_SSPP1, 21, 3 }, + { DBGBUS_SSPP1, 21, 4 }, + { DBGBUS_SSPP1, 21, 5 }, + { DBGBUS_SSPP1, 21, 6 }, + { DBGBUS_SSPP1, 21, 7 }, + + { DBGBUS_SSPP1, 22, 0 }, + { DBGBUS_SSPP1, 22, 1 }, + { DBGBUS_SSPP1, 22, 2 }, + { DBGBUS_SSPP1, 22, 3 }, + { DBGBUS_SSPP1, 22, 4 }, + { DBGBUS_SSPP1, 22, 5 }, + { DBGBUS_SSPP1, 22, 6 }, + { DBGBUS_SSPP1, 22, 7 }, + + { DBGBUS_SSPP1, 24, 0 }, + { DBGBUS_SSPP1, 24, 1 }, + { DBGBUS_SSPP1, 24, 2 }, + { DBGBUS_SSPP1, 24, 3 }, + { DBGBUS_SSPP1, 24, 4 }, + { DBGBUS_SSPP1, 24, 5 }, + { DBGBUS_SSPP1, 24, 6 }, + { DBGBUS_SSPP1, 24, 7 }, + + { DBGBUS_SSPP1, 25, 0 }, + { DBGBUS_SSPP1, 25, 1 }, + { DBGBUS_SSPP1, 25, 2 }, + { DBGBUS_SSPP1, 25, 3 }, + { DBGBUS_SSPP1, 25, 4 }, + { DBGBUS_SSPP1, 25, 5 }, + { DBGBUS_SSPP1, 25, 6 }, + { DBGBUS_SSPP1, 25, 7 }, + + /* dma 3 */ + { DBGBUS_SSPP1, 30, 0 }, + { DBGBUS_SSPP1, 30, 1 }, + { DBGBUS_SSPP1, 30, 2 }, + { DBGBUS_SSPP1, 30, 3 }, + { DBGBUS_SSPP1, 30, 4 }, + { DBGBUS_SSPP1, 30, 5 }, + { DBGBUS_SSPP1, 30, 6 }, + { DBGBUS_SSPP1, 30, 7 }, + + { DBGBUS_SSPP1, 31, 0 }, + { DBGBUS_SSPP1, 31, 1 }, + { DBGBUS_SSPP1, 31, 2 }, + { DBGBUS_SSPP1, 31, 3 }, + { DBGBUS_SSPP1, 31, 4 }, + { DBGBUS_SSPP1, 31, 5 }, + { DBGBUS_SSPP1, 31, 6 }, + { DBGBUS_SSPP1, 31, 7 }, + + { DBGBUS_SSPP1, 32, 0 }, + { DBGBUS_SSPP1, 32, 1 }, + { DBGBUS_SSPP1, 32, 2 }, + { DBGBUS_SSPP1, 32, 3 }, + { DBGBUS_SSPP1, 32, 4 }, + { DBGBUS_SSPP1, 32, 5 }, + { DBGBUS_SSPP1, 32, 6 }, + { DBGBUS_SSPP1, 32, 7 }, + + { DBGBUS_SSPP1, 33, 0 }, + { DBGBUS_SSPP1, 33, 1 }, + { DBGBUS_SSPP1, 33, 2 }, + { DBGBUS_SSPP1, 33, 3 }, + { DBGBUS_SSPP1, 33, 4 }, + { DBGBUS_SSPP1, 33, 5 }, + { DBGBUS_SSPP1, 33, 6 }, + { DBGBUS_SSPP1, 33, 7 }, + + { DBGBUS_SSPP1, 34, 0 }, + { DBGBUS_SSPP1, 34, 1 }, + { DBGBUS_SSPP1, 34, 2 }, + { DBGBUS_SSPP1, 34, 3 }, + { DBGBUS_SSPP1, 34, 4 }, + { DBGBUS_SSPP1, 34, 5 }, + { DBGBUS_SSPP1, 34, 6 }, + { DBGBUS_SSPP1, 34, 7 }, + + { DBGBUS_SSPP1, 35, 0 }, + { DBGBUS_SSPP1, 35, 1 }, + { DBGBUS_SSPP1, 35, 2 }, + + /* dma 1 */ + { DBGBUS_SSPP1, 40, 0 }, + { DBGBUS_SSPP1, 40, 1 }, + { DBGBUS_SSPP1, 40, 2 }, + { DBGBUS_SSPP1, 40, 3 }, + { DBGBUS_SSPP1, 40, 4 }, + { DBGBUS_SSPP1, 40, 5 }, + { DBGBUS_SSPP1, 40, 6 }, + { DBGBUS_SSPP1, 40, 7 }, + + { DBGBUS_SSPP1, 41, 0 }, + { DBGBUS_SSPP1, 41, 1 }, + { DBGBUS_SSPP1, 41, 2 }, + { DBGBUS_SSPP1, 41, 3 }, + { DBGBUS_SSPP1, 41, 4 }, + { DBGBUS_SSPP1, 41, 5 }, + { DBGBUS_SSPP1, 41, 6 }, + { DBGBUS_SSPP1, 41, 7 }, + + { DBGBUS_SSPP1, 42, 0 }, + { DBGBUS_SSPP1, 42, 1 }, + { DBGBUS_SSPP1, 42, 2 }, + { DBGBUS_SSPP1, 42, 3 }, + { DBGBUS_SSPP1, 42, 4 }, + { DBGBUS_SSPP1, 42, 5 }, + { DBGBUS_SSPP1, 42, 6 }, + { DBGBUS_SSPP1, 42, 7 }, + + { DBGBUS_SSPP1, 44, 0 }, + { DBGBUS_SSPP1, 44, 1 }, + { DBGBUS_SSPP1, 44, 2 }, + { DBGBUS_SSPP1, 44, 3 }, + { DBGBUS_SSPP1, 44, 4 }, + { DBGBUS_SSPP1, 44, 5 }, + { DBGBUS_SSPP1, 44, 6 }, + { DBGBUS_SSPP1, 44, 7 }, + + { DBGBUS_SSPP1, 45, 0 }, + { DBGBUS_SSPP1, 45, 1 }, + { DBGBUS_SSPP1, 45, 2 }, + { DBGBUS_SSPP1, 45, 3 }, + { DBGBUS_SSPP1, 45, 4 }, + { DBGBUS_SSPP1, 45, 5 }, + { DBGBUS_SSPP1, 45, 6 }, + { DBGBUS_SSPP1, 45, 7 }, + + /* cursor 1 */ + { DBGBUS_SSPP1, 80, 0 }, + { DBGBUS_SSPP1, 80, 1 }, + { DBGBUS_SSPP1, 80, 2 }, + { DBGBUS_SSPP1, 80, 3 }, + { DBGBUS_SSPP1, 80, 4 }, + { DBGBUS_SSPP1, 80, 5 }, + { DBGBUS_SSPP1, 80, 6 }, + { DBGBUS_SSPP1, 80, 7 }, + + { DBGBUS_SSPP1, 81, 0 }, + { DBGBUS_SSPP1, 81, 1 }, + { DBGBUS_SSPP1, 81, 2 }, + { DBGBUS_SSPP1, 81, 3 }, + { DBGBUS_SSPP1, 81, 4 }, + { DBGBUS_SSPP1, 81, 5 }, + { DBGBUS_SSPP1, 81, 6 }, + { DBGBUS_SSPP1, 81, 7 }, + + { DBGBUS_SSPP1, 82, 0 }, + { DBGBUS_SSPP1, 82, 1 }, + { DBGBUS_SSPP1, 82, 2 }, + { DBGBUS_SSPP1, 82, 3 }, + { DBGBUS_SSPP1, 82, 4 }, + { DBGBUS_SSPP1, 82, 5 }, + { DBGBUS_SSPP1, 82, 6 }, + { DBGBUS_SSPP1, 82, 7 }, + + { DBGBUS_SSPP1, 83, 0 }, + { DBGBUS_SSPP1, 83, 1 }, + { DBGBUS_SSPP1, 83, 2 }, + { DBGBUS_SSPP1, 83, 3 }, + { DBGBUS_SSPP1, 83, 4 }, + { DBGBUS_SSPP1, 83, 5 }, + { DBGBUS_SSPP1, 83, 6 }, + { DBGBUS_SSPP1, 83, 7 }, + + { DBGBUS_SSPP1, 84, 0 }, + { DBGBUS_SSPP1, 84, 1 }, + { DBGBUS_SSPP1, 84, 2 }, + { DBGBUS_SSPP1, 84, 3 }, + { DBGBUS_SSPP1, 84, 4 }, + { DBGBUS_SSPP1, 84, 5 }, + { DBGBUS_SSPP1, 84, 6 }, + { DBGBUS_SSPP1, 84, 7 }, + + /* dspp */ + { DBGBUS_DSPP, 13, 0 }, + { DBGBUS_DSPP, 19, 0 }, + { DBGBUS_DSPP, 14, 0 }, + { DBGBUS_DSPP, 14, 1 }, + { DBGBUS_DSPP, 14, 3 }, + { DBGBUS_DSPP, 20, 0 }, + { DBGBUS_DSPP, 20, 1 }, + { DBGBUS_DSPP, 20, 3 }, + + /* ppb_0 */ + { DBGBUS_DSPP, 31, 0 }, + { DBGBUS_DSPP, 33, 0 }, + { DBGBUS_DSPP, 35, 0 }, + { DBGBUS_DSPP, 42, 0 }, + + /* ppb_1 */ + { DBGBUS_DSPP, 32, 0 }, + { DBGBUS_DSPP, 34, 0 }, + { DBGBUS_DSPP, 36, 0 }, + { DBGBUS_DSPP, 43, 0 }, + + /* lm_lut */ + { DBGBUS_DSPP, 109, 0 }, + { DBGBUS_DSPP, 105, 0 }, + { DBGBUS_DSPP, 103, 0 }, + + /* tear-check */ + { DBGBUS_PERIPH, 63, 0 }, + { DBGBUS_PERIPH, 64, 0 }, + { DBGBUS_PERIPH, 65, 0 }, + { DBGBUS_PERIPH, 73, 0 }, + { DBGBUS_PERIPH, 74, 0 }, + + /* crossbar */ + { DBGBUS_DSPP, 0, 0}, + + /* rotator */ + { DBGBUS_DSPP, 9, 0}, + + /* blend */ + /* LM0 */ + { DBGBUS_DSPP, 63, 0}, + { DBGBUS_DSPP, 63, 1}, + { DBGBUS_DSPP, 63, 2}, + { DBGBUS_DSPP, 63, 3}, + { DBGBUS_DSPP, 63, 4}, + { DBGBUS_DSPP, 63, 5}, + { DBGBUS_DSPP, 63, 6}, + { DBGBUS_DSPP, 63, 7}, + + { DBGBUS_DSPP, 64, 0}, + { DBGBUS_DSPP, 64, 1}, + { DBGBUS_DSPP, 64, 2}, + { DBGBUS_DSPP, 64, 3}, + { DBGBUS_DSPP, 64, 4}, + { DBGBUS_DSPP, 64, 5}, + { DBGBUS_DSPP, 64, 6}, + { DBGBUS_DSPP, 64, 7}, + + { DBGBUS_DSPP, 65, 0}, + { DBGBUS_DSPP, 65, 1}, + { DBGBUS_DSPP, 65, 2}, + { DBGBUS_DSPP, 65, 3}, + { DBGBUS_DSPP, 65, 4}, + { DBGBUS_DSPP, 65, 5}, + { DBGBUS_DSPP, 65, 6}, + { DBGBUS_DSPP, 65, 7}, + + { DBGBUS_DSPP, 66, 0}, + { DBGBUS_DSPP, 66, 1}, + { DBGBUS_DSPP, 66, 2}, + { DBGBUS_DSPP, 66, 3}, + { DBGBUS_DSPP, 66, 4}, + { DBGBUS_DSPP, 66, 5}, + { DBGBUS_DSPP, 66, 6}, + { DBGBUS_DSPP, 66, 7}, + + { DBGBUS_DSPP, 67, 0}, + { DBGBUS_DSPP, 67, 1}, + { DBGBUS_DSPP, 67, 2}, + { DBGBUS_DSPP, 67, 3}, + { DBGBUS_DSPP, 67, 4}, + { DBGBUS_DSPP, 67, 5}, + { DBGBUS_DSPP, 67, 6}, + { DBGBUS_DSPP, 67, 7}, + + { DBGBUS_DSPP, 68, 0}, + { DBGBUS_DSPP, 68, 1}, + { DBGBUS_DSPP, 68, 2}, + { DBGBUS_DSPP, 68, 3}, + { DBGBUS_DSPP, 68, 4}, + { DBGBUS_DSPP, 68, 5}, + { DBGBUS_DSPP, 68, 6}, + { DBGBUS_DSPP, 68, 7}, + + { DBGBUS_DSPP, 69, 0}, + { DBGBUS_DSPP, 69, 1}, + { DBGBUS_DSPP, 69, 2}, + { DBGBUS_DSPP, 69, 3}, + { DBGBUS_DSPP, 69, 4}, + { DBGBUS_DSPP, 69, 5}, + { DBGBUS_DSPP, 69, 6}, + { DBGBUS_DSPP, 69, 7}, + + /* LM1 */ + { DBGBUS_DSPP, 70, 0}, + { DBGBUS_DSPP, 70, 1}, + { DBGBUS_DSPP, 70, 2}, + { DBGBUS_DSPP, 70, 3}, + { DBGBUS_DSPP, 70, 4}, + { DBGBUS_DSPP, 70, 5}, + { DBGBUS_DSPP, 70, 6}, + { DBGBUS_DSPP, 70, 7}, + + { DBGBUS_DSPP, 71, 0}, + { DBGBUS_DSPP, 71, 1}, + { DBGBUS_DSPP, 71, 2}, + { DBGBUS_DSPP, 71, 3}, + { DBGBUS_DSPP, 71, 4}, + { DBGBUS_DSPP, 71, 5}, + { DBGBUS_DSPP, 71, 6}, + { DBGBUS_DSPP, 71, 7}, + + { DBGBUS_DSPP, 72, 0}, + { DBGBUS_DSPP, 72, 1}, + { DBGBUS_DSPP, 72, 2}, + { DBGBUS_DSPP, 72, 3}, + { DBGBUS_DSPP, 72, 4}, + { DBGBUS_DSPP, 72, 5}, + { DBGBUS_DSPP, 72, 6}, + { DBGBUS_DSPP, 72, 7}, + + { DBGBUS_DSPP, 73, 0}, + { DBGBUS_DSPP, 73, 1}, + { DBGBUS_DSPP, 73, 2}, + { DBGBUS_DSPP, 73, 3}, + { DBGBUS_DSPP, 73, 4}, + { DBGBUS_DSPP, 73, 5}, + { DBGBUS_DSPP, 73, 6}, + { DBGBUS_DSPP, 73, 7}, + + { DBGBUS_DSPP, 74, 0}, + { DBGBUS_DSPP, 74, 1}, + { DBGBUS_DSPP, 74, 2}, + { DBGBUS_DSPP, 74, 3}, + { DBGBUS_DSPP, 74, 4}, + { DBGBUS_DSPP, 74, 5}, + { DBGBUS_DSPP, 74, 6}, + { DBGBUS_DSPP, 74, 7}, + + { DBGBUS_DSPP, 75, 0}, + { DBGBUS_DSPP, 75, 1}, + { DBGBUS_DSPP, 75, 2}, + { DBGBUS_DSPP, 75, 3}, + { DBGBUS_DSPP, 75, 4}, + { DBGBUS_DSPP, 75, 5}, + { DBGBUS_DSPP, 75, 6}, + { DBGBUS_DSPP, 75, 7}, + + { DBGBUS_DSPP, 76, 0}, + { DBGBUS_DSPP, 76, 1}, + { DBGBUS_DSPP, 76, 2}, + { DBGBUS_DSPP, 76, 3}, + { DBGBUS_DSPP, 76, 4}, + { DBGBUS_DSPP, 76, 5}, + { DBGBUS_DSPP, 76, 6}, + { DBGBUS_DSPP, 76, 7}, + + /* LM2 */ + { DBGBUS_DSPP, 77, 0}, + { DBGBUS_DSPP, 77, 1}, + { DBGBUS_DSPP, 77, 2}, + { DBGBUS_DSPP, 77, 3}, + { DBGBUS_DSPP, 77, 4}, + { DBGBUS_DSPP, 77, 5}, + { DBGBUS_DSPP, 77, 6}, + { DBGBUS_DSPP, 77, 7}, + + { DBGBUS_DSPP, 78, 0}, + { DBGBUS_DSPP, 78, 1}, + { DBGBUS_DSPP, 78, 2}, + { DBGBUS_DSPP, 78, 3}, + { DBGBUS_DSPP, 78, 4}, + { DBGBUS_DSPP, 78, 5}, + { DBGBUS_DSPP, 78, 6}, + { DBGBUS_DSPP, 78, 7}, + + { DBGBUS_DSPP, 79, 0}, + { DBGBUS_DSPP, 79, 1}, + { DBGBUS_DSPP, 79, 2}, + { DBGBUS_DSPP, 79, 3}, + { DBGBUS_DSPP, 79, 4}, + { DBGBUS_DSPP, 79, 5}, + { DBGBUS_DSPP, 79, 6}, + { DBGBUS_DSPP, 79, 7}, + + { DBGBUS_DSPP, 80, 0}, + { DBGBUS_DSPP, 80, 1}, + { DBGBUS_DSPP, 80, 2}, + { DBGBUS_DSPP, 80, 3}, + { DBGBUS_DSPP, 80, 4}, + { DBGBUS_DSPP, 80, 5}, + { DBGBUS_DSPP, 80, 6}, + { DBGBUS_DSPP, 80, 7}, + + { DBGBUS_DSPP, 81, 0}, + { DBGBUS_DSPP, 81, 1}, + { DBGBUS_DSPP, 81, 2}, + { DBGBUS_DSPP, 81, 3}, + { DBGBUS_DSPP, 81, 4}, + { DBGBUS_DSPP, 81, 5}, + { DBGBUS_DSPP, 81, 6}, + { DBGBUS_DSPP, 81, 7}, + + { DBGBUS_DSPP, 82, 0}, + { DBGBUS_DSPP, 82, 1}, + { DBGBUS_DSPP, 82, 2}, + { DBGBUS_DSPP, 82, 3}, + { DBGBUS_DSPP, 82, 4}, + { DBGBUS_DSPP, 82, 5}, + { DBGBUS_DSPP, 82, 6}, + { DBGBUS_DSPP, 82, 7}, + + { DBGBUS_DSPP, 83, 0}, + { DBGBUS_DSPP, 83, 1}, + { DBGBUS_DSPP, 83, 2}, + { DBGBUS_DSPP, 83, 3}, + { DBGBUS_DSPP, 83, 4}, + { DBGBUS_DSPP, 83, 5}, + { DBGBUS_DSPP, 83, 6}, + { DBGBUS_DSPP, 83, 7}, + + /* csc */ + { DBGBUS_SSPP0, 7, 0}, + { DBGBUS_SSPP0, 7, 1}, + { DBGBUS_SSPP0, 27, 0}, + { DBGBUS_SSPP0, 27, 1}, + { DBGBUS_SSPP1, 7, 0}, + { DBGBUS_SSPP1, 7, 1}, + { DBGBUS_SSPP1, 27, 0}, + { DBGBUS_SSPP1, 27, 1}, + + /* pcc */ + { DBGBUS_SSPP0, 3, 3}, + { DBGBUS_SSPP0, 23, 3}, + { DBGBUS_SSPP0, 33, 3}, + { DBGBUS_SSPP0, 43, 3}, + { DBGBUS_SSPP1, 3, 3}, + { DBGBUS_SSPP1, 23, 3}, + { DBGBUS_SSPP1, 33, 3}, + { DBGBUS_SSPP1, 43, 3}, + + /* spa */ + { DBGBUS_SSPP0, 8, 0}, + { DBGBUS_SSPP0, 28, 0}, + { DBGBUS_SSPP1, 8, 0}, + { DBGBUS_SSPP1, 28, 0}, + { DBGBUS_DSPP, 13, 0}, + { DBGBUS_DSPP, 19, 0}, + + /* igc */ + { DBGBUS_SSPP0, 9, 0}, + { DBGBUS_SSPP0, 9, 1}, + { DBGBUS_SSPP0, 9, 3}, + { DBGBUS_SSPP0, 29, 0}, + { DBGBUS_SSPP0, 29, 1}, + { DBGBUS_SSPP0, 29, 3}, + { DBGBUS_SSPP0, 17, 0}, + { DBGBUS_SSPP0, 17, 1}, + { DBGBUS_SSPP0, 17, 3}, + { DBGBUS_SSPP0, 37, 0}, + { DBGBUS_SSPP0, 37, 1}, + { DBGBUS_SSPP0, 37, 3}, + { DBGBUS_SSPP0, 46, 0}, + { DBGBUS_SSPP0, 46, 1}, + { DBGBUS_SSPP0, 46, 3}, + + { DBGBUS_SSPP1, 9, 0}, + { DBGBUS_SSPP1, 9, 1}, + { DBGBUS_SSPP1, 9, 3}, + { DBGBUS_SSPP1, 29, 0}, + { DBGBUS_SSPP1, 29, 1}, + { DBGBUS_SSPP1, 29, 3}, + { DBGBUS_SSPP1, 17, 0}, + { DBGBUS_SSPP1, 17, 1}, + { DBGBUS_SSPP1, 17, 3}, + { DBGBUS_SSPP1, 37, 0}, + { DBGBUS_SSPP1, 37, 1}, + { DBGBUS_SSPP1, 37, 3}, + { DBGBUS_SSPP1, 46, 0}, + { DBGBUS_SSPP1, 46, 1}, + { DBGBUS_SSPP1, 46, 3}, + + { DBGBUS_DSPP, 14, 0}, + { DBGBUS_DSPP, 14, 1}, + { DBGBUS_DSPP, 14, 3}, + { DBGBUS_DSPP, 20, 0}, + { DBGBUS_DSPP, 20, 1}, + { DBGBUS_DSPP, 20, 3}, + + { DBGBUS_PERIPH, 60, 0}, +}; + +static struct vbif_debug_bus_entry vbif_dbg_bus_msm8998[] = { + {0x214, 0x21c, 16, 2, 0x0, 0xd}, /* arb clients */ + {0x214, 0x21c, 16, 2, 0x80, 0xc0}, /* arb clients */ + {0x214, 0x21c, 16, 2, 0x100, 0x140}, /* arb clients */ + {0x214, 0x21c, 0, 16, 0x0, 0xf}, /* xin blocks - axi side */ + {0x214, 0x21c, 0, 16, 0x80, 0xa4}, /* xin blocks - axi side */ + {0x214, 0x21c, 0, 15, 0x100, 0x124}, /* xin blocks - axi side */ + {0x21c, 0x214, 0, 14, 0, 0xc}, /* xin blocks - clock side */ +}; + +/** + * _sde_dbg_enable_power - use callback to turn power on for hw register access + * @enable: whether to turn power on or off + */ +static inline void _sde_dbg_enable_power(int enable) +{ + if (!sde_dbg_base.power_ctrl.enable_fn) + return; + sde_dbg_base.power_ctrl.enable_fn( + sde_dbg_base.power_ctrl.handle, + sde_dbg_base.power_ctrl.client, + enable); +} + +/** + * _sde_dump_reg - helper function for dumping rotator register set content + * @dump_name: register set name + * @reg_dump_flag: dumping flag controlling in-log/memory dump location + * @base_addr: starting address of io region for calculating offsets to print + * @addr: starting address offset for dumping + * @len_bytes: range of the register set + * @dump_mem: output buffer for memory dump location option + * @from_isr: whether being called from isr context + */ +static void _sde_dump_reg(const char *dump_name, u32 reg_dump_flag, + char __iomem *base_addr, char __iomem *addr, size_t len_bytes, + u32 **dump_mem, bool from_isr) +{ + u32 in_log, in_mem, len_align, len_padded; + u32 *dump_addr = NULL; + char __iomem *end_addr; + int i; + + if (!len_bytes) + return; + + in_log = (reg_dump_flag & SDE_DBG_DUMP_IN_LOG); + in_mem = (reg_dump_flag & SDE_DBG_DUMP_IN_MEM); + + pr_debug("%s: reg_dump_flag=%d in_log=%d in_mem=%d\n", + dump_name, reg_dump_flag, in_log, in_mem); + + if (!in_log && !in_mem) + return; + + if (in_log) + dev_info(sde_dbg_base.dev, "%s: start_offset 0x%lx len 0x%zx\n", + dump_name, addr - base_addr, len_bytes); + + len_align = (len_bytes + REG_DUMP_ALIGN - 1) / REG_DUMP_ALIGN; + len_padded = len_align * REG_DUMP_ALIGN; + end_addr = addr + len_bytes; + + if (in_mem) { + if (dump_mem && !(*dump_mem)) { + phys_addr_t phys = 0; + *dump_mem = dma_alloc_coherent(sde_dbg_base.dev, + len_padded, &phys, GFP_KERNEL); + } + + if (dump_mem && *dump_mem) { + dump_addr = *dump_mem; + dev_info(sde_dbg_base.dev, + "%s: start_addr:0x%pK len:0x%x reg_offset=0x%lx\n", + dump_name, dump_addr, len_padded, + addr - base_addr); + } else { + in_mem = 0; + pr_err("dump_mem: kzalloc fails!\n"); + } + } + + if (!from_isr) + _sde_dbg_enable_power(true); + + for (i = 0; i < len_align; i++) { + u32 x0, x4, x8, xc; + + x0 = (addr < end_addr) ? readl_relaxed(addr + 0x0) : 0; + x4 = (addr + 0x4 < end_addr) ? readl_relaxed(addr + 0x4) : 0; + x8 = (addr + 0x8 < end_addr) ? readl_relaxed(addr + 0x8) : 0; + xc = (addr + 0xc < end_addr) ? readl_relaxed(addr + 0xc) : 0; + + if (in_log) + dev_info(sde_dbg_base.dev, + "0x%lx : %08x %08x %08x %08x\n", + addr - base_addr, x0, x4, x8, xc); + + if (dump_addr) { + dump_addr[i * 4] = x0; + dump_addr[i * 4 + 1] = x4; + dump_addr[i * 4 + 2] = x8; + dump_addr[i * 4 + 3] = xc; + } + + addr += REG_DUMP_ALIGN; + } + + if (!from_isr) + _sde_dbg_enable_power(false); +} + +/** + * _sde_dbg_get_dump_range - helper to retrieve dump length for a range node + * @range_node: range node to dump + * @max_offset: max offset of the register base + * @Return: length + */ +static u32 _sde_dbg_get_dump_range(struct sde_dbg_reg_offset *range_node, + size_t max_offset) +{ + u32 length = 0; + + if ((range_node->start > range_node->end) || + (range_node->end > max_offset) || (range_node->start == 0 + && range_node->end == 0)) { + length = max_offset; + } else { + length = range_node->end - range_node->start; + } + + return length; +} + +static int _sde_dump_reg_range_cmp(void *priv, struct list_head *a, + struct list_head *b) +{ + struct sde_dbg_reg_range *ar, *br; + + if (!a || !b) + return 0; + + ar = container_of(a, struct sde_dbg_reg_range, head); + br = container_of(b, struct sde_dbg_reg_range, head); + + return ar->offset.start - br->offset.start; +} + +/** + * _sde_dump_reg_by_ranges - dump ranges or full range of the register blk base + * @dbg: register blk base structure + * @reg_dump_flag: dump target, memory, kernel log, or both + */ +static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg, + u32 reg_dump_flag) +{ + char __iomem *addr; + size_t len; + struct sde_dbg_reg_range *range_node; + + if (!dbg || !dbg->base) { + pr_err("dbg base is null!\n"); + return; + } + + dev_info(sde_dbg_base.dev, "%s:=========%s DUMP=========\n", __func__, + dbg->name); + + /* If there is a list to dump the registers by ranges, use the ranges */ + if (!list_empty(&dbg->sub_range_list)) { + /* sort the list by start address first */ + list_sort(NULL, &dbg->sub_range_list, _sde_dump_reg_range_cmp); + list_for_each_entry(range_node, &dbg->sub_range_list, head) { + len = _sde_dbg_get_dump_range(&range_node->offset, + dbg->max_offset); + addr = dbg->base + range_node->offset.start; + pr_debug("%s: range_base=0x%pK start=0x%x end=0x%x\n", + range_node->range_name, + addr, range_node->offset.start, + range_node->offset.end); + + _sde_dump_reg(range_node->range_name, reg_dump_flag, + dbg->base, addr, len, + &range_node->reg_dump, false); + } + } else { + /* If there is no list to dump ranges, dump all registers */ + dev_info(sde_dbg_base.dev, + "Ranges not found, will dump full registers\n"); + dev_info(sde_dbg_base.dev, "base:0x%pK len:0x%zx\n", dbg->base, + dbg->max_offset); + addr = dbg->base; + len = dbg->max_offset; + _sde_dump_reg(dbg->name, reg_dump_flag, dbg->base, addr, len, + &dbg->reg_dump, false); + } +} + +/** + * _sde_dump_reg_by_blk - dump a named register base region + * @blk_name: register blk name + */ +static void _sde_dump_reg_by_blk(const char *blk_name) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *blk_base; + + if (!dbg_base) + return; + + list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) { + if (strlen(blk_base->name) && + !strcmp(blk_base->name, blk_name)) { + _sde_dump_reg_by_ranges(blk_base, + dbg_base->enable_reg_dump); + break; + } + } +} + +/** + * _sde_dump_reg_all - dump all register regions + */ +static void _sde_dump_reg_all(void) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *blk_base; + + if (!dbg_base) + return; + + list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) + if (strlen(blk_base->name)) + _sde_dump_reg_by_blk(blk_base->name); +} + +/** + * _sde_dump_get_blk_addr - retrieve register block address by name + * @blk_name: register blk name + * @Return: register blk base, or NULL + */ +static struct sde_dbg_reg_base *_sde_dump_get_blk_addr(const char *blk_name) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *blk_base; + + list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) + if (strlen(blk_base->name) && !strcmp(blk_base->name, blk_name)) + return blk_base; + + return NULL; +} + +static void _sde_dbg_dump_sde_dbg_bus(struct sde_dbg_sde_debug_bus *bus) +{ + bool in_log, in_mem; + u32 **dump_mem = NULL; + u32 *dump_addr = NULL; + u32 status = 0; + struct sde_debug_bus_entry *head; + phys_addr_t phys = 0; + int list_size; + int i; + u32 offset; + void __iomem *mem_base = NULL; + struct sde_dbg_reg_base *reg_base; + + if (!bus || !bus->cmn.entries_size) + return; + + list_for_each_entry(reg_base, &sde_dbg_base.reg_base_list, + reg_base_head) + if (strlen(reg_base->name) && + !strcmp(reg_base->name, bus->cmn.name)) + mem_base = reg_base->base + bus->top_blk_off; + + if (!mem_base) { + pr_err("unable to find mem_base for %s\n", bus->cmn.name); + return; + } + + dump_mem = &bus->cmn.dumped_content; + + /* will keep in memory 4 entries of 4 bytes each */ + list_size = (bus->cmn.entries_size * 4 * 4); + + in_log = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_LOG); + in_mem = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_MEM); + + if (!in_log && !in_mem) + return; + + dev_info(sde_dbg_base.dev, "======== start %s dump =========\n", + bus->cmn.name); + + if (in_mem) { + if (!(*dump_mem)) + *dump_mem = dma_alloc_coherent(sde_dbg_base.dev, + list_size, &phys, GFP_KERNEL); + + if (*dump_mem) { + dump_addr = *dump_mem; + dev_info(sde_dbg_base.dev, + "%s: start_addr:0x%pK len:0x%x\n", + __func__, dump_addr, list_size); + } else { + in_mem = false; + pr_err("dump_mem: allocation fails\n"); + } + } + + _sde_dbg_enable_power(true); + for (i = 0; i < bus->cmn.entries_size; i++) { + head = bus->entries + i; + writel_relaxed(TEST_MASK(head->block_id, head->test_id), + mem_base + head->wr_addr); + wmb(); /* make sure test bits were written */ + + if (bus->cmn.flags & DBGBUS_FLAGS_DSPP) + offset = DBGBUS_DSPP_STATUS; + else + offset = head->wr_addr + 0x4; + + status = readl_relaxed(mem_base + offset); + + if (in_log) + dev_info(sde_dbg_base.dev, + "waddr=0x%x blk=%d tst=%d val=0x%x\n", + head->wr_addr, head->block_id, + head->test_id, status); + + if (dump_addr && in_mem) { + dump_addr[i*4] = head->wr_addr; + dump_addr[i*4 + 1] = head->block_id; + dump_addr[i*4 + 2] = head->test_id; + dump_addr[i*4 + 3] = status; + } + + /* Disable debug bus once we are done */ + writel_relaxed(0, mem_base + head->wr_addr); + + } + _sde_dbg_enable_power(false); + + dev_info(sde_dbg_base.dev, "======== end %s dump =========\n", + bus->cmn.name); +} + +static void _sde_dbg_dump_vbif_debug_bus_entry( + struct vbif_debug_bus_entry *head, void __iomem *mem_base, + u32 *dump_addr, bool in_log) +{ + int i, j; + u32 val; + + if (!dump_addr && !in_log) + return; + + for (i = 0; i < head->block_cnt; i++) { + writel_relaxed(1 << (i + head->bit_offset), + mem_base + head->block_bus_addr); + /* make sure that current bus blcok enable */ + wmb(); + for (j = head->test_pnt_start; j < head->test_pnt_cnt; j++) { + writel_relaxed(j, mem_base + head->block_bus_addr + 4); + /* make sure that test point is enabled */ + wmb(); + val = readl_relaxed(mem_base + MMSS_VBIF_TEST_BUS_OUT); + if (dump_addr) { + *dump_addr++ = head->block_bus_addr; + *dump_addr++ = i; + *dump_addr++ = j; + *dump_addr++ = val; + } + if (in_log) + dev_info(sde_dbg_base.dev, + "testpoint:%x arb/xin id=%d index=%d val=0x%x\n", + head->block_bus_addr, i, j, val); + } + } +} + +static void _sde_dbg_dump_vbif_dbg_bus(struct sde_dbg_vbif_debug_bus *bus) +{ + bool in_log, in_mem; + u32 **dump_mem = NULL; + u32 *dump_addr = NULL; + u32 value; + struct vbif_debug_bus_entry *head; + phys_addr_t phys = 0; + int i, list_size = 0; + void __iomem *mem_base = NULL; + struct vbif_debug_bus_entry *dbg_bus; + u32 bus_size; + struct sde_dbg_reg_base *reg_base; + + if (!bus || !bus->cmn.entries_size) + return; + + list_for_each_entry(reg_base, &sde_dbg_base.reg_base_list, + reg_base_head) + if (strlen(reg_base->name) && + !strcmp(reg_base->name, bus->cmn.name)) + mem_base = reg_base->base; + + if (!mem_base) { + pr_err("unable to find mem_base for %s\n", bus->cmn.name); + return; + } + + dbg_bus = bus->entries; + bus_size = bus->cmn.entries_size; + list_size = bus->cmn.entries_size; + dump_mem = &bus->cmn.dumped_content; + + dev_info(sde_dbg_base.dev, "======== start %s dump =========\n", + bus->cmn.name); + + if (!dump_mem || !dbg_bus || !bus_size || !list_size) + return; + + /* allocate memory for each test point */ + for (i = 0; i < bus_size; i++) { + head = dbg_bus + i; + list_size += (head->block_cnt * head->test_pnt_cnt); + } + + /* 4 bytes * 4 entries for each test point*/ + list_size *= 16; + + in_log = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_LOG); + in_mem = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_MEM); + + if (!in_log && !in_mem) + return; + + if (in_mem) { + if (!(*dump_mem)) + *dump_mem = dma_alloc_coherent(sde_dbg_base.dev, + list_size, &phys, GFP_KERNEL); + + if (*dump_mem) { + dump_addr = *dump_mem; + dev_info(sde_dbg_base.dev, + "%s: start_addr:0x%pK len:0x%x\n", + __func__, dump_addr, list_size); + } else { + in_mem = false; + pr_err("dump_mem: allocation fails\n"); + } + } + + _sde_dbg_enable_power(true); + + value = readl_relaxed(mem_base + MMSS_VBIF_CLKON); + writel_relaxed(value | BIT(1), mem_base + MMSS_VBIF_CLKON); + + /* make sure that vbif core is on */ + wmb(); + + for (i = 0; i < bus_size; i++) { + head = dbg_bus + i; + + writel_relaxed(0, mem_base + head->disable_bus_addr); + writel_relaxed(BIT(0), mem_base + MMSS_VBIF_TEST_BUS_OUT_CTRL); + /* make sure that other bus is off */ + wmb(); + + _sde_dbg_dump_vbif_debug_bus_entry(head, mem_base, dump_addr, + in_log); + if (dump_addr) + dump_addr += (head->block_cnt * head->test_pnt_cnt * 4); + } + + _sde_dbg_enable_power(false); + + dev_info(sde_dbg_base.dev, "======== end %s dump =========\n", + bus->cmn.name); +} + +/** + * _sde_dump_array - dump array of register bases + * @blk_arr: array of register base pointers + * @len: length of blk_arr + * @do_panic: whether to trigger a panic after dumping + * @name: string indicating origin of dump + * @dump_dbgbus_sde: whether to dump the sde debug bus + * @dump_dbgbus_vbif_rt: whether to dump the vbif rt debug bus + */ +static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], + u32 len, bool do_panic, const char *name, bool dump_dbgbus_sde, + bool dump_dbgbus_vbif_rt) +{ + int i; + + for (i = 0; i < len; i++) { + if (blk_arr[i] != NULL) + _sde_dump_reg_by_ranges(blk_arr[i], + sde_dbg_base.enable_reg_dump); + } + + sde_evtlog_dump_all(sde_dbg_base.evtlog); + + if (dump_dbgbus_sde) + _sde_dbg_dump_sde_dbg_bus(&sde_dbg_base.dbgbus_sde); + + if (dump_dbgbus_vbif_rt) + _sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt); + + if (do_panic && sde_dbg_base.panic_on_err) + panic(name); +} + +/** + * _sde_dump_work - deferred dump work function + * @work: work structure + */ +static void _sde_dump_work(struct work_struct *work) +{ + _sde_dump_array(sde_dbg_base.req_dump_blks, + ARRAY_SIZE(sde_dbg_base.req_dump_blks), + sde_dbg_base.work_panic, "evtlog_workitem", + sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work, + sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work); +} + +void sde_dbg_dump(bool queue_work, const char *name, ...) +{ + int i, index = 0; + bool do_panic = false; + bool dump_dbgbus_sde = false; + bool dump_dbgbus_vbif_rt = false; + va_list args; + char *blk_name = NULL; + struct sde_dbg_reg_base *blk_base = NULL; + struct sde_dbg_reg_base **blk_arr; + u32 blk_len; + + if (!sde_evtlog_is_enabled(sde_dbg_base.evtlog, SDE_EVTLOG_DEFAULT)) + return; + + if (queue_work && work_pending(&sde_dbg_base.dump_work)) + return; + + blk_arr = &sde_dbg_base.req_dump_blks[0]; + blk_len = ARRAY_SIZE(sde_dbg_base.req_dump_blks); + + memset(sde_dbg_base.req_dump_blks, 0, + sizeof(sde_dbg_base.req_dump_blks)); + + va_start(args, name); + i = 0; + while ((blk_name = va_arg(args, char*))) { + if (i++ >= SDE_EVTLOG_MAX_DATA) { + pr_err("could not parse all dump arguments\n"); + break; + } + if (IS_ERR_OR_NULL(blk_name)) + break; + + blk_base = _sde_dump_get_blk_addr(blk_name); + if (blk_base) { + if (index < blk_len) { + blk_arr[index] = blk_base; + index++; + } else { + pr_err("insufficient space to to dump %s\n", + blk_name); + } + } + + if (!strcmp(blk_name, "dbg_bus")) + dump_dbgbus_sde = true; + + if (!strcmp(blk_name, "vbif_dbg_bus")) + dump_dbgbus_vbif_rt = true; + + if (!strcmp(blk_name, "panic")) + do_panic = true; + } + va_end(args); + + if (queue_work) { + /* schedule work to dump later */ + sde_dbg_base.work_panic = do_panic; + sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work = + dump_dbgbus_sde; + sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work = + dump_dbgbus_vbif_rt; + schedule_work(&sde_dbg_base.dump_work); + } else { + _sde_dump_array(blk_arr, blk_len, do_panic, name, + dump_dbgbus_sde, dump_dbgbus_vbif_rt); + } +} + +/* + * sde_dbg_debugfs_open - debugfs open handler for evtlog dump + * @inode: debugfs inode + * @file: file handle + */ +static int sde_dbg_debugfs_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + file->private_data = inode->i_private; + return 0; +} + +/** + * sde_evtlog_dump_read - debugfs read handler for evtlog dump + * @file: file handler + * @buff: user buffer content for debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + ssize_t len = 0; + char evtlog_buf[SDE_EVTLOG_BUF_MAX]; + + len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf, + SDE_EVTLOG_BUF_MAX); + if (copy_to_user(buff, evtlog_buf, len)) + return -EFAULT; + *ppos += len; + + return len; +} + +/** + * sde_evtlog_dump_write - debugfs write handler for evtlog dump + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_evtlog_dump_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + _sde_dump_reg_all(); + + sde_evtlog_dump_all(sde_dbg_base.evtlog); + + _sde_dbg_dump_sde_dbg_bus(&sde_dbg_base.dbgbus_sde); + _sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt); + + if (sde_dbg_base.panic_on_err) + panic("sde"); + + return count; +} + +static const struct file_operations sde_evtlog_fops = { + .open = sde_dbg_debugfs_open, + .read = sde_evtlog_dump_read, + .write = sde_evtlog_dump_write, +}; + +void sde_dbg_init_dbg_buses(u32 hwversion) +{ + static struct sde_dbg_base *dbg = &sde_dbg_base; + char debug_name[80] = ""; + + memset(&dbg->dbgbus_sde, 0, sizeof(dbg->dbgbus_sde)); + memset(&dbg->dbgbus_vbif_rt, 0, sizeof(dbg->dbgbus_vbif_rt)); + + switch (hwversion) { + case SDE_HW_VER_300: + case SDE_HW_VER_301: + dbg->dbgbus_sde.entries = dbg_bus_sde_8998; + dbg->dbgbus_sde.cmn.entries_size = ARRAY_SIZE(dbg_bus_sde_8998); + dbg->dbgbus_sde.cmn.flags = DBGBUS_FLAGS_DSPP; + + dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998; + dbg->dbgbus_vbif_rt.cmn.entries_size = + ARRAY_SIZE(vbif_dbg_bus_msm8998); + break; + default: + pr_err("unsupported chipset id %u\n", hwversion); + break; + } + + if (dbg->dbgbus_sde.entries) { + dbg->dbgbus_sde.cmn.name = DBGBUS_NAME_SDE; + snprintf(debug_name, sizeof(debug_name), "%s_dbgbus", + dbg->dbgbus_sde.cmn.name); + dbg->dbgbus_sde.cmn.enable_mask = DEFAULT_DBGBUS_SDE; + debugfs_create_u32(debug_name, 0600, dbg->root, + &dbg->dbgbus_sde.cmn.enable_mask); + } + + if (dbg->dbgbus_vbif_rt.entries) { + dbg->dbgbus_vbif_rt.cmn.name = DBGBUS_NAME_VBIF_RT; + snprintf(debug_name, sizeof(debug_name), "%s_dbgbus", + dbg->dbgbus_vbif_rt.cmn.name); + dbg->dbgbus_vbif_rt.cmn.enable_mask = DEFAULT_DBGBUS_VBIFRT; + debugfs_create_u32(debug_name, 0600, dbg->root, + &dbg->dbgbus_vbif_rt.cmn.enable_mask); + } +} + +int sde_dbg_init(struct dentry *debugfs_root, struct device *dev, + struct sde_dbg_power_ctrl *power_ctrl) +{ + int i; + + INIT_LIST_HEAD(&sde_dbg_base.reg_base_list); + sde_dbg_base.dev = dev; + sde_dbg_base.power_ctrl = *power_ctrl; + + + sde_dbg_base.evtlog = sde_evtlog_init(); + if (IS_ERR_OR_NULL(sde_dbg_base.evtlog)) + return PTR_ERR(sde_dbg_base.evtlog); + + sde_dbg_base_evtlog = sde_dbg_base.evtlog; + + sde_dbg_base.root = debugfs_create_dir("evt_dbg", debugfs_root); + if (IS_ERR_OR_NULL(sde_dbg_base.root)) { + pr_err("debugfs_create_dir fail, error %ld\n", + PTR_ERR(sde_dbg_base.root)); + sde_dbg_base.root = NULL; + return -ENODEV; + } + + INIT_WORK(&sde_dbg_base.dump_work, _sde_dump_work); + sde_dbg_base.work_panic = false; + + for (i = 0; i < SDE_EVTLOG_ENTRY; i++) + sde_dbg_base.evtlog->logs[i].counter = i; + + debugfs_create_file("dump", 0600, sde_dbg_base.root, NULL, + &sde_evtlog_fops); + debugfs_create_u32("enable", 0600, sde_dbg_base.root, + &(sde_dbg_base.evtlog->enable)); + debugfs_create_u32("panic", 0600, sde_dbg_base.root, + &sde_dbg_base.panic_on_err); + debugfs_create_u32("reg_dump", 0600, sde_dbg_base.root, + &sde_dbg_base.enable_reg_dump); + + sde_dbg_base.panic_on_err = DEFAULT_PANIC; + sde_dbg_base.enable_reg_dump = DEFAULT_REGDUMP; + + pr_info("evtlog_status: enable:%d, panic:%d, dump:%d\n", + sde_dbg_base.evtlog->enable, sde_dbg_base.panic_on_err, + sde_dbg_base.enable_reg_dump); + + return 0; +} + +/** + * sde_dbg_destroy - destroy sde debug facilities + */ +void sde_dbg_destroy(void) +{ + debugfs_remove_recursive(sde_dbg_base.root); + sde_dbg_base.root = NULL; + + sde_dbg_base_evtlog = NULL; + sde_evtlog_destroy(sde_dbg_base.evtlog); + sde_dbg_base.evtlog = NULL; +} + +/** + * sde_dbg_reg_base_release - release allocated reg dump file private data + * @inode: debugfs inode + * @file: file handle + * @Return: 0 on success + */ +static int sde_dbg_reg_base_release(struct inode *inode, struct file *file) +{ + struct sde_dbg_reg_base *dbg = file->private_data; + + if (dbg && dbg->buf) { + kfree(dbg->buf); + dbg->buf_len = 0; + dbg->buf = NULL; + } + return 0; +} + + +/** + * sde_dbg_reg_base_offset_write - set new offset and len to debugfs reg base + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_reg_base_offset_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct sde_dbg_reg_base *dbg = file->private_data; + u32 off = 0; + u32 cnt = DEFAULT_BASE_REG_CNT; + char buf[24]; + + if (!dbg) + return -ENODEV; + + if (count >= sizeof(buf)) + return -EFAULT; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + if (sscanf(buf, "%5x %x", &off, &cnt) != 2) + return -EFAULT; + + if (off > dbg->max_offset) + return -EINVAL; + + if (off % sizeof(u32)) + return -EINVAL; + + if (cnt > (dbg->max_offset - off)) + cnt = dbg->max_offset - off; + + if (cnt % sizeof(u32)) + return -EINVAL; + + dbg->off = off; + dbg->cnt = cnt; + + pr_debug("offset=%x cnt=%x\n", off, cnt); + + return count; +} + +/** + * sde_dbg_reg_base_offset_read - read current offset and len of register base + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_reg_base_offset_read(struct file *file, + char __user *buff, size_t count, loff_t *ppos) +{ + struct sde_dbg_reg_base *dbg = file->private_data; + int len = 0; + char buf[24] = {'\0'}; + + if (!dbg) + return -ENODEV; + + if (*ppos) + return 0; /* the end */ + + len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt); + if (len < 0 || len >= sizeof(buf)) + return 0; + + if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; /* increase offset */ + + return len; +} + +/** + * sde_dbg_reg_base_reg_write - write to reg base hw at offset a given value + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_reg_base_reg_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct sde_dbg_reg_base *dbg = file->private_data; + size_t off; + u32 data, cnt; + char buf[24]; + + if (!dbg) + return -ENODEV; + + if (count >= sizeof(buf)) + return -EFAULT; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + cnt = sscanf(buf, "%zx %x", &off, &data); + + if (cnt < 2) + return -EFAULT; + + if (off >= dbg->max_offset) + return -EFAULT; + + _sde_dbg_enable_power(true); + + writel_relaxed(data, dbg->base + off); + + _sde_dbg_enable_power(false); + + pr_debug("addr=%zx data=%x\n", off, data); + + return count; +} + +/** + * sde_dbg_reg_base_reg_read - read len from reg base hw at current offset + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_reg_base_reg_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct sde_dbg_reg_base *dbg = file->private_data; + size_t len; + + if (!dbg) { + pr_err("invalid handle\n"); + return -ENODEV; + } + + if (!dbg->buf) { + char *hwbuf, *hwbuf_cur; + char dump_buf[64]; + char __iomem *ioptr; + int cnt, tot; + + dbg->buf_len = sizeof(dump_buf) * + DIV_ROUND_UP(dbg->cnt, ROW_BYTES); + + if (dbg->buf_len % sizeof(u32)) + return -EINVAL; + + dbg->buf = kzalloc(dbg->buf_len, GFP_KERNEL); + + if (!dbg->buf) + return -ENOMEM; + + hwbuf = kzalloc(dbg->buf_len, GFP_KERNEL); + if (!hwbuf) { + kfree(dbg->buf); + return -ENOMEM; + } + hwbuf_cur = hwbuf; + + ioptr = dbg->base + dbg->off; + tot = 0; + + _sde_dbg_enable_power(true); + + memcpy_fromio(hwbuf, ioptr, dbg->buf_len); + + _sde_dbg_enable_power(false); + + for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) { + hex_dump_to_buffer(hwbuf_cur, + min(cnt, ROW_BYTES), + ROW_BYTES, GROUP_BYTES, dump_buf, + sizeof(dump_buf), false); + len = scnprintf(dbg->buf + tot, dbg->buf_len - tot, + "0x%08x: %s\n", + ((int) (unsigned long) hwbuf_cur) - + ((int) (unsigned long) dbg->base), + dump_buf); + + hwbuf_cur += ROW_BYTES; + tot += len; + if (tot >= dbg->buf_len) + break; + } + + dbg->buf_len = tot; + kfree(hwbuf); + } + + if (*ppos >= dbg->buf_len) + return 0; /* done reading */ + + len = min(count, dbg->buf_len - (size_t) *ppos); + if (copy_to_user(user_buf, dbg->buf + *ppos, len)) { + pr_err("failed to copy to user\n"); + return -EFAULT; + } + + *ppos += len; /* increase offset */ + + return len; +} + +static const struct file_operations sde_off_fops = { + .open = sde_dbg_debugfs_open, + .release = sde_dbg_reg_base_release, + .read = sde_dbg_reg_base_offset_read, + .write = sde_dbg_reg_base_offset_write, +}; + +static const struct file_operations sde_reg_fops = { + .open = sde_dbg_debugfs_open, + .release = sde_dbg_reg_base_release, + .read = sde_dbg_reg_base_reg_read, + .write = sde_dbg_reg_base_reg_write, +}; + +int sde_dbg_reg_register_base(const char *name, void __iomem *base, + size_t max_offset) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *reg_base; + struct dentry *ent_off, *ent_reg; + char dn[80] = ""; + int prefix_len = 0; + + reg_base = kzalloc(sizeof(*reg_base), GFP_KERNEL); + if (!reg_base) + return -ENOMEM; + + if (name) + strlcpy(reg_base->name, name, sizeof(reg_base->name)); + reg_base->base = base; + reg_base->max_offset = max_offset; + reg_base->off = 0; + reg_base->cnt = DEFAULT_BASE_REG_CNT; + reg_base->reg_dump = NULL; + + if (name) + prefix_len = snprintf(dn, sizeof(dn), "%s_", name); + strlcpy(dn + prefix_len, "off", sizeof(dn) - prefix_len); + ent_off = debugfs_create_file(dn, 0600, dbg_base->root, reg_base, + &sde_off_fops); + if (IS_ERR_OR_NULL(ent_off)) { + pr_err("debugfs_create_file: offset fail\n"); + goto off_fail; + } + + strlcpy(dn + prefix_len, "reg", sizeof(dn) - prefix_len); + ent_reg = debugfs_create_file(dn, 0600, dbg_base->root, reg_base, + &sde_reg_fops); + if (IS_ERR_OR_NULL(ent_reg)) { + pr_err("debugfs_create_file: reg fail\n"); + goto reg_fail; + } + + /* Initialize list to make sure check for null list will be valid */ + INIT_LIST_HEAD(®_base->sub_range_list); + + pr_debug("%s base: %pK max_offset 0x%zX\n", reg_base->name, + reg_base->base, reg_base->max_offset); + + list_add(®_base->reg_base_head, &dbg_base->reg_base_list); + + return 0; +reg_fail: + debugfs_remove(ent_off); +off_fail: + kfree(reg_base); + return -ENODEV; +} + +void sde_dbg_reg_register_dump_range(const char *base_name, + const char *range_name, u32 offset_start, u32 offset_end, + uint32_t xin_id) +{ + struct sde_dbg_reg_base *reg_base; + struct sde_dbg_reg_range *range; + + reg_base = _sde_dump_get_blk_addr(base_name); + if (!reg_base) { + pr_err("error: for range %s unable to locate base %s\n", + range_name, base_name); + return; + } + + if (!range_name || strlen(range_name) == 0) { + pr_err("%pS: bad range name, base_name %s, offset_start 0x%X, end 0x%X\n", + __builtin_return_address(0), base_name, + offset_start, offset_end); + return; + } + + if (offset_end - offset_start < REG_DUMP_ALIGN || + offset_start > offset_end) { + pr_err("%pS: bad range, base_name %s, range_name %s, offset_start 0x%X, end 0x%X\n", + __builtin_return_address(0), base_name, + range_name, offset_start, offset_end); + return; + } + + range = kzalloc(sizeof(*range), GFP_KERNEL); + if (!range) + return; + + strlcpy(range->range_name, range_name, sizeof(range->range_name)); + range->offset.start = offset_start; + range->offset.end = offset_end; + range->xin_id = xin_id; + list_add_tail(&range->head, ®_base->sub_range_list); + + pr_debug("base %s, range %s, start 0x%X, end 0x%X\n", + base_name, range->range_name, + range->offset.start, range->offset.end); +} + +void sde_dbg_set_sde_top_offset(u32 blk_off) +{ + sde_dbg_base.dbgbus_sde.top_blk_off = blk_off; +} diff --git a/drivers/gpu/drm/msm/sde_dbg.h b/drivers/gpu/drm/msm/sde_dbg.h index 271c41f05ce5..74fd4c94b490 100644 --- a/drivers/gpu/drm/msm/sde_dbg.h +++ b/drivers/gpu/drm/msm/sde_dbg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,34 +29,288 @@ enum sde_dbg_evtlog_flag { SDE_EVTLOG_ALL = BIT(7) }; +enum sde_dbg_dump_flag { + SDE_DBG_DUMP_IN_LOG = BIT(0), + SDE_DBG_DUMP_IN_MEM = BIT(1), +}; + +#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG +#define SDE_EVTLOG_DEFAULT_ENABLE 1 +#else +#define SDE_EVTLOG_DEFAULT_ENABLE 0 +#endif + +/* + * evtlog will print this number of entries when it is called through + * sysfs node or panic. This prevents kernel log from evtlog message + * flood. + */ +#define SDE_EVTLOG_PRINT_ENTRY 256 + +/* + * evtlog keeps this number of entries in memory for debug purpose. This + * number must be greater than print entry to prevent out of bound evtlog + * entry array access. + */ +#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4) +#define SDE_EVTLOG_MAX_DATA 15 +#define SDE_EVTLOG_BUF_MAX 512 +#define SDE_EVTLOG_BUF_ALIGN 32 + +struct sde_dbg_power_ctrl { + void *handle; + void *client; + int (*enable_fn)(void *handle, void *client, bool enable); +}; + +struct sde_dbg_evtlog_log { + u32 counter; + s64 time; + const char *name; + int line; + u32 data[SDE_EVTLOG_MAX_DATA]; + u32 data_cnt; + int pid; +}; + +struct sde_dbg_evtlog { + struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY]; + u32 first; + u32 last; + u32 curr; + u32 next; + u32 enable; + spinlock_t spin_lock; +}; + +extern struct sde_dbg_evtlog *sde_dbg_base_evtlog; + +/** + * SDE_EVT32 - Write a list of 32bit values to the event log, default area + * ... - variable arguments + */ +#define SDE_EVT32(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \ + __LINE__, SDE_EVTLOG_DEFAULT, ##__VA_ARGS__, \ + SDE_EVTLOG_DATA_LIMITER) + /** - * SDE_EVT32 - Write an list of 32bit values as an event into the event log + * SDE_EVT32_IRQ - Write a list of 32bit values to the event log, IRQ area * ... - variable arguments */ -#define SDE_EVT32(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_DEFAULT, \ - ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER) -#define SDE_EVT32_IRQ(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_IRQ, \ - ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER) +#define SDE_EVT32_IRQ(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \ + __LINE__, SDE_EVTLOG_IRQ, ##__VA_ARGS__, \ + SDE_EVTLOG_DATA_LIMITER) -#define SDE_DBG_DUMP(...) \ - sde_dbg_dump(false, __func__, ##__VA_ARGS__, \ +/** + * SDE_DBG_DUMP - trigger dumping of all sde_dbg facilities + * @va_args: list of named register dump ranges and regions to dump, as + * registered previously through sde_dbg_reg_register_base and + * sde_dbg_reg_register_dump_range. + * Including the special name "panic" will trigger a panic after + * the dumping work has completed. + */ +#define SDE_DBG_DUMP(...) sde_dbg_dump(false, __func__, ##__VA_ARGS__, \ SDE_DBG_DUMP_DATA_LIMITER) -#define SDE_DBG_DUMP_WQ(...) \ - sde_dbg_dump(true, __func__, ##__VA_ARGS__, \ +/** + * SDE_DBG_DUMP_WQ - trigger dumping of all sde_dbg facilities, queuing the work + * @va_args: list of named register dump ranges and regions to dump, as + * registered previously through sde_dbg_reg_register_base and + * sde_dbg_reg_register_dump_range. + * Including the special name "panic" will trigger a panic after + * the dumping work has completed. + */ +#define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \ SDE_DBG_DUMP_DATA_LIMITER) #if defined(CONFIG_DEBUG_FS) -int sde_evtlog_init(struct dentry *debugfs_root); -void sde_evtlog_destroy(void); -void sde_evtlog(const char *name, int line, int flag, ...); -void sde_dbg_dump(bool queue, const char *name, ...); +/** + * sde_evtlog_init - allocate a new event log object + * Returns: evtlog or -ERROR + */ +struct sde_dbg_evtlog *sde_evtlog_init(void); + +/** + * sde_evtlog_destroy - destroy previously allocated event log + * @evtlog: pointer to evtlog + * Returns: none + */ +void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog); + +/** + * sde_evtlog_log - log an entry into the event log. + * log collection may be enabled/disabled entirely via debugfs + * log area collection may be filtered by user provided flags via debugfs. + * @evtlog: pointer to evtlog + * @name: function name of call site + * @line: line number of call site + * @flag: log area filter flag checked against user's debugfs request + * Returns: none + */ +void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line, + int flag, ...); + +/** + * sde_evtlog_dump_all - print all entries in event log to kernel log + * @evtlog: pointer to evtlog + * Returns: none + */ +void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog); + +/** + * sde_evtlog_is_enabled - check whether log collection is enabled for given + * event log and log area flag + * @evtlog: pointer to evtlog + * @flag: log area filter flag + * Returns: none + */ +bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag); + +/** + * sde_evtlog_dump_to_buffer - print content of event log to the given buffer + * @evtlog: pointer to evtlog + * @evtlog_buf: target buffer to print into + * @evtlog_buf_size: size of target buffer + * Returns: number of bytes written to buffer + */ +ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, + char *evtlog_buf, ssize_t evtlog_buf_size); + +/** + * sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset + * @hwversion: Chipset revision + */ +void sde_dbg_init_dbg_buses(u32 hwversion); + +/** + * sde_dbg_init - initialize global sde debug facilities: evtlog, regdump + * @debugfs_root: debugfs root in which to create sde debug entries + * @dev: device handle + * @power_ctrl: power control callback structure for enabling clocks + * during register dumping + * Returns: 0 or -ERROR + */ +int sde_dbg_init(struct dentry *debugfs_root, struct device *dev, + struct sde_dbg_power_ctrl *power_ctrl); + +/** + * sde_dbg_destroy - destroy the global sde debug facilities + * Returns: none + */ +void sde_dbg_destroy(void); + +/** + * sde_dbg_dump - trigger dumping of all sde_dbg facilities + * @queue_work: whether to queue the dumping work to the work_struct + * @name: string indicating origin of dump + * @va_args: list of named register dump ranges and regions to dump, as + * registered previously through sde_dbg_reg_register_base and + * sde_dbg_reg_register_dump_range. + * Including the special name "panic" will trigger a panic after + * the dumping work has completed. + * Returns: none + */ +void sde_dbg_dump(bool queue_work, const char *name, ...); + +/** + * sde_dbg_reg_register_base - register a hw register address section for later + * dumping. call this before calling sde_dbg_reg_register_dump_range + * to be able to specify sub-ranges within the base hw range. + * @name: name of base region + * @base: base pointer of region + * @max_offset: length of region + * Returns: 0 or -ERROR + */ +int sde_dbg_reg_register_base(const char *name, void __iomem *base, + size_t max_offset); + +/** + * sde_dbg_reg_register_dump_range - register a hw register sub-region for + * later register dumping associated with base specified by + * sde_dbg_reg_register_base + * @base_name: name of base region + * @range_name: name of sub-range within base region + * @offset_start: sub-range's start offset from base's base pointer + * @offset_end: sub-range's end offset from base's base pointer + * @xin_id: xin id + * Returns: none + */ +void sde_dbg_reg_register_dump_range(const char *base_name, + const char *range_name, u32 offset_start, u32 offset_end, + uint32_t xin_id); + +/** + * sde_dbg_set_sde_top_offset - set the target specific offset from mdss base + * address of the top registers. Used for accessing debug bus controls. + * @blk_off: offset from mdss base of the top block + */ +void sde_dbg_set_sde_top_offset(u32 blk_off); #else -static inline int sde_evtlog_init(struct dentry *debugfs_root) { return 0; } -static inline void sde_evtlog(const char *name, int line, flag, ...) {} -static inline void sde_evtlog_destroy(void) { } -static inline void sde_dbg_dump(bool queue, const char *name, ...) {} -#endif +static inline struct sde_dbg_evtlog *sde_evtlog_init(void) +{ + return NULL; +} + +static inline void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog) +{ +} + +static inline void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, + const char *name, int line, int flag, ...) +{ +} + +static inline void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) +{ +} + +static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, + u32 flag) +{ + return false; +} + +static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, + char *evtlog_buf, ssize_t evtlog_buf_size) +{ + return 0; +} + +void sde_dbg_init_dbg_buses(u32 hwversion) +{ +} + +static inline int sde_dbg_init(struct dentry *debugfs_root, struct device *dev, + struct sde_dbg_power_ctrl *power_ctrl) +{ + return 0; +} + +static inline void sde_dbg_destroy(void) +{ +} + +static inline void sde_dbg_dump(bool queue_work, const char *name, ...) +{ +} + +static inline int sde_dbg_reg_register_base(const char *name, + void __iomem *base, size_t max_offset) +{ + return 0; +} + +static inline void sde_dbg_reg_register_dump_range(const char *base_name, + const char *range_name, u32 offset_start, u32 offset_end, + uint32_t xin_id) +{ +} + +void sde_dbg_set_sde_top_offset(u32 blk_off) +{ +} +#endif /* defined(CONFIG_DEBUG_FS) */ + #endif /* SDE_DBG_H_ */ diff --git a/drivers/gpu/drm/msm/sde_dbg_evtlog.c b/drivers/gpu/drm/msm/sde_dbg_evtlog.c index 72832776659d..759bdab48840 100644 --- a/drivers/gpu/drm/msm/sde_dbg_evtlog.c +++ b/drivers/gpu/drm/msm/sde_dbg_evtlog.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -10,7 +10,7 @@ * GNU General Public License for more details. */ -#define pr_fmt(fmt) "sde_evtlog:[%s] " fmt, __func__ +#define pr_fmt(fmt) "sde_dbg:[%s] " fmt, __func__ #include <linux/delay.h> #include <linux/spinlock.h> @@ -18,77 +18,36 @@ #include <linux/debugfs.h> #include <linux/uaccess.h> #include <linux/dma-buf.h> +#include <linux/slab.h> #include "sde_dbg.h" #include "sde_trace.h" -#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG -#define SDE_EVTLOG_DEFAULT_ENABLE 1 -#else -#define SDE_EVTLOG_DEFAULT_ENABLE 0 -#endif - -#define SDE_DBG_DEFAULT_PANIC 1 - -/* - * evtlog will print this number of entries when it is called through - * sysfs node or panic. This prevents kernel log from evtlog message - * flood. - */ -#define SDE_EVTLOG_PRINT_ENTRY 256 - -/* - * evtlog keeps this number of entries in memory for debug purpose. This - * number must be greater than print entry to prevent out of bound evtlog - * entry array access. - */ -#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4) -#define SDE_EVTLOG_MAX_DATA 15 -#define SDE_EVTLOG_BUF_MAX 512 -#define SDE_EVTLOG_BUF_ALIGN 32 - -DEFINE_SPINLOCK(sde_evtloglock); - -struct tlog { - u32 counter; - s64 time; - const char *name; - int line; - u32 data[SDE_EVTLOG_MAX_DATA]; - u32 data_cnt; - int pid; -}; - -static struct sde_dbg_evtlog { - struct tlog logs[SDE_EVTLOG_ENTRY]; - u32 first; - u32 last; - u32 curr; - struct dentry *evtlog; - u32 evtlog_enable; - u32 panic_on_err; - struct work_struct evtlog_dump_work; - bool work_panic; -} sde_dbg_evtlog; - -static inline bool sde_evtlog_is_enabled(u32 flag) +bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag) { - return (flag & sde_dbg_evtlog.evtlog_enable) || - (flag == SDE_EVTLOG_ALL && sde_dbg_evtlog.evtlog_enable); + if (!evtlog) + return false; + + return (flag & evtlog->enable) || + (flag == SDE_EVTLOG_ALL && evtlog->enable); } -void sde_evtlog(const char *name, int line, int flag, ...) +void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line, + int flag, ...) { unsigned long flags; int i, val = 0; va_list args; - struct tlog *log; + struct sde_dbg_evtlog_log *log; + + if (!evtlog) + return; - if (!sde_evtlog_is_enabled(flag)) + if (!sde_evtlog_is_enabled(evtlog, flag)) return; - spin_lock_irqsave(&sde_evtloglock, flags); - log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.curr]; + spin_lock_irqsave(&evtlog->spin_lock, flags); + log = &evtlog->logs[evtlog->curr]; log->time = ktime_to_us(ktime_get()); log->name = name; log->line = line; @@ -106,26 +65,27 @@ void sde_evtlog(const char *name, int line, int flag, ...) } va_end(args); log->data_cnt = i; - sde_dbg_evtlog.curr = (sde_dbg_evtlog.curr + 1) % SDE_EVTLOG_ENTRY; - sde_dbg_evtlog.last++; + evtlog->curr = (evtlog->curr + 1) % SDE_EVTLOG_ENTRY; + evtlog->last++; trace_sde_evtlog(name, line, i > 0 ? log->data[0] : 0, i > 1 ? log->data[1] : 0); - spin_unlock_irqrestore(&sde_evtloglock, flags); + spin_unlock_irqrestore(&evtlog->spin_lock, flags); } /* always dump the last entries which are not dumped yet */ -static bool _sde_evtlog_dump_calc_range(void) +static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog) { - static u32 next; bool need_dump = true; unsigned long flags; - struct sde_dbg_evtlog *evtlog = &sde_dbg_evtlog; - spin_lock_irqsave(&sde_evtloglock, flags); + if (!evtlog) + return false; + + spin_lock_irqsave(&evtlog->spin_lock, flags); - evtlog->first = next; + evtlog->first = evtlog->next; if (evtlog->last == evtlog->first) { need_dump = false; @@ -143,27 +103,34 @@ static bool _sde_evtlog_dump_calc_range(void) evtlog->last - evtlog->first); evtlog->first = evtlog->last - SDE_EVTLOG_PRINT_ENTRY; } - next = evtlog->first + 1; + evtlog->next = evtlog->first + 1; dump_exit: - spin_unlock_irqrestore(&sde_evtloglock, flags); + spin_unlock_irqrestore(&evtlog->spin_lock, flags); return need_dump; } -static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size) +ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, + char *evtlog_buf, ssize_t evtlog_buf_size) { int i; ssize_t off = 0; - struct tlog *log, *prev_log; + struct sde_dbg_evtlog_log *log, *prev_log; unsigned long flags; - spin_lock_irqsave(&sde_evtloglock, flags); + if (!evtlog || !evtlog_buf) + return 0; - log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.first % - SDE_EVTLOG_ENTRY]; + /* update markers, exit if nothing to print */ + if (!_sde_evtlog_dump_calc_range(evtlog)) + return 0; + + spin_lock_irqsave(&evtlog->spin_lock, flags); - prev_log = &sde_dbg_evtlog.logs[(sde_dbg_evtlog.first - 1) % + log = &evtlog->logs[evtlog->first % SDE_EVTLOG_ENTRY]; + + prev_log = &evtlog->logs[(evtlog->first - 1) % SDE_EVTLOG_ENTRY]; off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d", @@ -175,7 +142,7 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size) } off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), - "=>[%-8d:%-11llu:%9llu][%-4d]:", sde_dbg_evtlog.first, + "=>[%-8d:%-11llu:%9llu][%-4d]:", evtlog->first, log->time, (log->time - prev_log->time), log->pid); for (i = 0; i < log->data_cnt; i++) @@ -184,143 +151,37 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size) off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n"); - spin_unlock_irqrestore(&sde_evtloglock, flags); + spin_unlock_irqrestore(&evtlog->spin_lock, flags); return off; } -static void _sde_evtlog_dump_all(void) -{ - char evtlog_buf[SDE_EVTLOG_BUF_MAX]; - - while (_sde_evtlog_dump_calc_range()) { - sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX); - pr_info("%s", evtlog_buf); - } -} - -static void _sde_dump_array(bool dead, const char *name) -{ - _sde_evtlog_dump_all(); - - if (dead && sde_dbg_evtlog.panic_on_err) - panic(name); -} - -static void _sde_dump_work(struct work_struct *work) +void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) { - _sde_dump_array(sde_dbg_evtlog.work_panic, "evtlog_workitem"); -} - -void sde_dbg_dump(bool queue, const char *name, ...) -{ - int i; - bool dead = false; - va_list args; - char *blk_name = NULL; - - if (!sde_evtlog_is_enabled(SDE_EVTLOG_DEFAULT)) - return; + char buf[SDE_EVTLOG_BUF_MAX]; - if (queue && work_pending(&sde_dbg_evtlog.evtlog_dump_work)) + if (!evtlog) return; - va_start(args, name); - for (i = 0; i < SDE_EVTLOG_MAX_DATA; i++) { - blk_name = va_arg(args, char*); - if (IS_ERR_OR_NULL(blk_name)) - break; - - if (!strcmp(blk_name, "panic")) - dead = true; - } - va_end(args); - - if (queue) { - /* schedule work to dump later */ - sde_dbg_evtlog.work_panic = dead; - schedule_work(&sde_dbg_evtlog.evtlog_dump_work); - } else { - _sde_dump_array(dead, name); - } + while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf))) + pr_info("%s", buf); } -static int sde_evtlog_dump_open(struct inode *inode, struct file *file) +struct sde_dbg_evtlog *sde_evtlog_init(void) { - /* non-seekable */ - file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); - file->private_data = inode->i_private; - return 0; -} - -static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff, - size_t count, loff_t *ppos) -{ - ssize_t len = 0; - char evtlog_buf[SDE_EVTLOG_BUF_MAX]; - - if (_sde_evtlog_dump_calc_range()) { - len = sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX); - if (copy_to_user(buff, evtlog_buf, len)) - return -EFAULT; - *ppos += len; - } - - return len; -} - -static ssize_t sde_evtlog_dump_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - _sde_evtlog_dump_all(); - - if (sde_dbg_evtlog.panic_on_err) - panic("sde"); - - return count; -} - -static const struct file_operations sde_evtlog_fops = { - .open = sde_evtlog_dump_open, - .read = sde_evtlog_dump_read, - .write = sde_evtlog_dump_write, -}; - -int sde_evtlog_init(struct dentry *debugfs_root) -{ - int i; - - sde_dbg_evtlog.evtlog = debugfs_create_dir("evt_dbg", debugfs_root); - if (IS_ERR_OR_NULL(sde_dbg_evtlog.evtlog)) { - pr_err("debugfs_create_dir fail, error %ld\n", - PTR_ERR(sde_dbg_evtlog.evtlog)); - sde_dbg_evtlog.evtlog = NULL; - return -ENODEV; - } - - INIT_WORK(&sde_dbg_evtlog.evtlog_dump_work, _sde_dump_work); - sde_dbg_evtlog.work_panic = false; - - for (i = 0; i < SDE_EVTLOG_ENTRY; i++) - sde_dbg_evtlog.logs[i].counter = i; - - debugfs_create_file("dump", 0644, sde_dbg_evtlog.evtlog, NULL, - &sde_evtlog_fops); - debugfs_create_u32("enable", 0644, sde_dbg_evtlog.evtlog, - &sde_dbg_evtlog.evtlog_enable); - debugfs_create_u32("panic", 0644, sde_dbg_evtlog.evtlog, - &sde_dbg_evtlog.panic_on_err); + struct sde_dbg_evtlog *evtlog; - sde_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE; - sde_dbg_evtlog.panic_on_err = SDE_DBG_DEFAULT_PANIC; + evtlog = kzalloc(sizeof(*evtlog), GFP_KERNEL); + if (!evtlog) + return ERR_PTR(-ENOMEM); - pr_info("evtlog_status: enable:%d, panic:%d\n", - sde_dbg_evtlog.evtlog_enable, sde_dbg_evtlog.panic_on_err); + spin_lock_init(&evtlog->spin_lock); + evtlog->enable = SDE_EVTLOG_DEFAULT_ENABLE; - return 0; + return evtlog; } -void sde_evtlog_destroy(void) +void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog) { - debugfs_remove(sde_dbg_evtlog.evtlog); + kfree(evtlog); } diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c index ca9229ede251..50667c5921a0 100644 --- a/drivers/gpu/drm/msm/sde_edid_parser.c +++ b/drivers/gpu/drm/msm/sde_edid_parser.c @@ -229,10 +229,17 @@ u32 video_format) { u8 cea_mode = 0; struct drm_display_mode *mode; + u32 mode_fmt_flags = 0; /* Need to add Y420 support flag to the modes */ list_for_each_entry(mode, &connector->probed_modes, head) { + /* Cache the format flags before clearing */ + mode_fmt_flags = mode->flags; + /* Clear the RGB/YUV format flags before calling upstream API */ + mode->flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK; cea_mode = drm_match_cea_mode(mode); + /* Restore the format flags */ + mode->flags = mode_fmt_flags; if ((cea_mode != 0) && (cea_mode == video_format)) { SDE_EDID_DEBUG("%s found match for %d ", __func__, video_format); @@ -246,7 +253,7 @@ struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl, const u8 *db) { u32 offset = 0; - u8 len = 0; + u8 cmdb_len = 0; u8 svd_len = 0; const u8 *svd = NULL; u32 i = 0, j = 0; @@ -262,10 +269,8 @@ const u8 *db) return; } SDE_EDID_DEBUG("%s +\n", __func__); - len = db[0] & 0x1f; + cmdb_len = db[0] & 0x1f; - if (len < 7) - return; /* Byte 3 to L+1 contain SVDs */ offset += 2; @@ -273,20 +278,24 @@ const u8 *db) if (svd) { /*moving to the next byte as vic info begins there*/ - ++svd; svd_len = svd[0] & 0x1f; + ++svd; } for (i = 0; i < svd_len; i++, j++) { - video_format = *svd & 0x7F; - if (db[offset] & (1 << j)) + video_format = *(svd + i) & 0x7F; + if (cmdb_len == 1) { + /* If cmdb_len is 1, it means all SVDs support YUV */ + sde_edid_set_y420_support(connector, video_format); + } else if (db[offset] & (1 << j)) { sde_edid_set_y420_support(connector, video_format); - if (j & 0x80) { - j = j/8; - offset++; - if (offset >= len) - break; + if (j & 0x80) { + j = j/8; + offset++; + if (offset >= cmdb_len) + break; + } } } @@ -540,6 +549,12 @@ int _sde_edid_update_modes(struct drm_connector *connector, { int rc = 0; struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input); + struct drm_display_info *disp_info; + + disp_info = &connector->display_info; + + if (disp_info) + disp_info->edid_hdmi_dc_modes = 0; SDE_EDID_DEBUG("%s +", __func__); if (edid_ctrl->edid) { diff --git a/drivers/gpu/drm/msm/sde_edid_parser.h b/drivers/gpu/drm/msm/sde_edid_parser.h index 1143dc2c7bec..59e3dceca33c 100644 --- a/drivers/gpu/drm/msm/sde_edid_parser.h +++ b/drivers/gpu/drm/msm/sde_edid_parser.h @@ -33,6 +33,8 @@ #define SDE_CEA_EXT 0x02 #define SDE_EXTENDED_TAG 0x07 +#define SDE_DRM_MODE_FLAG_FMT_MASK (0x3 << 20) + enum extended_data_block_types { VIDEO_CAPABILITY_DATA_BLOCK = 0x0, VENDOR_SPECIFIC_VIDEO_DATA_BLOCK = 0x01, diff --git a/drivers/gpu/drm/msm/sde_hdcp_1x.c b/drivers/gpu/drm/msm/sde_hdcp_1x.c index 3aba9e307732..d08cf13c448d 100644 --- a/drivers/gpu/drm/msm/sde_hdcp_1x.c +++ b/drivers/gpu/drm/msm/sde_hdcp_1x.c @@ -751,11 +751,24 @@ error: static void sde_hdcp_1x_enable_sink_irq_hpd(struct sde_hdcp_1x *hdcp) { int rc; + u8 const required_major = 1, required_minor = 2; + u8 sink_major = 0, sink_minor = 0; u8 enable_hpd_irq = 0x1; + u16 version; - if (hdcp->current_tp.ds_type != DS_REPEATER) + if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) return; + version = *hdcp->init_data.version; + sink_major = (version >> 4) & 0x0f; + sink_minor = version & 0x0f; + + if ((sink_minor < required_minor) || (sink_major < required_major) || + (hdcp->current_tp.ds_type != DS_REPEATER)) { + pr_debug("sink irq hpd not enabled\n"); + return; + } + rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.ainfo, &enable_hpd_irq); if (IS_ERR_VALUE(rc)) SDE_HDCP_DEBUG("error writing ainfo to sink\n"); @@ -1295,6 +1308,11 @@ static void sde_hdcp_1x_auth_work(struct work_struct *work) if (rc) goto end; + +end: + if (rc && !sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) + hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL; + /* * Disabling software DDC before going into part3 to make sure * there is no Arbitration between software and hardware for DDC @@ -1302,9 +1320,6 @@ static void sde_hdcp_1x_auth_work(struct work_struct *work) if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io, HDMI_DDC_ARBITRATION) | (BIT(4))); -end: - if (rc && !sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) - hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL; sde_hdcp_1x_update_auth_status(hdcp); } @@ -1498,6 +1513,10 @@ static int sde_hdcp_1x_isr(void *input) SDE_HDCP_DEBUG("%s: AUTH FAIL, LINK0_STATUS=0x%08x\n", SDE_HDCP_STATE_NAME, link_status); + /* Clear AUTH_FAIL_INFO as well */ + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->auth_fail_info_ack)); + if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) { hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL; sde_hdcp_1x_update_auth_status(hdcp); @@ -1505,9 +1524,6 @@ static int sde_hdcp_1x_isr(void *input) complete_all(&hdcp->r0_checked); } - /* Clear AUTH_FAIL_INFO as well */ - DSS_REG_W(io, isr->int_reg, - (hdcp_int_val | isr->auth_fail_info_ack)); } if (hdcp_int_val & isr->tx_req_int) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c index c794b2c2d21e..6d8f21290aa2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c @@ -129,7 +129,7 @@ gf100_bar_init(struct nvkm_bar *base) if (bar->bar[0].mem) { addr = nvkm_memory_addr(bar->bar[0].mem) >> 12; - nvkm_wr32(device, 0x001714, 0xc0000000 | addr); + nvkm_wr32(device, 0x001714, 0x80000000 | addr); } return 0; diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index d4ac8c837314..8e86cf7da614 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -30,6 +30,7 @@ #include "radeon_audio.h" #include "atom.h" #include <linux/backlight.h> +#include <linux/dmi.h> extern int atom_debug; @@ -2183,9 +2184,17 @@ int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder, int fe_idx) goto assigned; } - /* on DCE32 and encoder can driver any block so just crtc id */ + /* + * On DCE32 any encoder can drive any block so usually just use crtc id, + * but Apple thinks different at least on iMac10,1, so there use linkb, + * otherwise the internal eDP panel will stay dark. + */ if (ASIC_IS_DCE32(rdev)) { - enc_idx = radeon_crtc->crtc_id; + if (dmi_match(DMI_PRODUCT_NAME, "iMac10,1")) + enc_idx = (dig->linkb) ? 1 : 0; + else + enc_idx = radeon_crtc->crtc_id; + goto assigned; } diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 3c32f095a873..2ccf81168d1e 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -782,6 +782,12 @@ bool ci_dpm_vblank_too_short(struct radeon_device *rdev) if (r600_dpm_get_vrefresh(rdev) > 120) return true; + /* disable mclk switching if the refresh is >120Hz, even if the + * blanking period would allow it + */ + if (r600_dpm_get_vrefresh(rdev) > 120) + return true; + if (vblank_time < switch_limit) return true; else diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 48cb19949ca3..9255b9c096b6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -282,26 +282,6 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) * Page Flip */ -void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, - struct drm_file *file) -{ - struct drm_pending_vblank_event *event; - struct drm_device *dev = rcrtc->crtc.dev; - unsigned long flags; - - /* Destroy the pending vertical blanking event associated with the - * pending page flip, if any, and disable vertical blanking interrupts. - */ - spin_lock_irqsave(&dev->event_lock, flags); - event = rcrtc->event; - if (event && event->base.file_priv == file) { - rcrtc->event = NULL; - event->base.destroy(&event->base); - drm_crtc_vblank_put(&rcrtc->crtc); - } - spin_unlock_irqrestore(&dev->event_lock, flags); -} - static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) { struct drm_pending_vblank_event *event; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 4b95d9d08c49..2bbe3f5aab65 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -67,8 +67,6 @@ enum rcar_du_output { int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index); void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); -void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, - struct drm_file *file); void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 40422f6b645e..bf4674aa6405 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -144,91 +144,6 @@ MODULE_DEVICE_TABLE(of, rcar_du_of_table); * DRM operations */ -static int rcar_du_unload(struct drm_device *dev) -{ - struct rcar_du_device *rcdu = dev->dev_private; - - if (rcdu->fbdev) - drm_fbdev_cma_fini(rcdu->fbdev); - - drm_kms_helper_poll_fini(dev); - drm_mode_config_cleanup(dev); - drm_vblank_cleanup(dev); - - dev->irq_enabled = 0; - dev->dev_private = NULL; - - return 0; -} - -static int rcar_du_load(struct drm_device *dev, unsigned long flags) -{ - struct platform_device *pdev = dev->platformdev; - struct device_node *np = pdev->dev.of_node; - struct rcar_du_device *rcdu; - struct resource *mem; - int ret; - - if (np == NULL) { - dev_err(dev->dev, "no platform data\n"); - return -ENODEV; - } - - rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL); - if (rcdu == NULL) { - dev_err(dev->dev, "failed to allocate private data\n"); - return -ENOMEM; - } - - init_waitqueue_head(&rcdu->commit.wait); - - rcdu->dev = &pdev->dev; - rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data; - rcdu->ddev = dev; - dev->dev_private = rcdu; - - /* I/O resources */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(rcdu->mmio)) - return PTR_ERR(rcdu->mmio); - - /* Initialize vertical blanking interrupts handling. Start with vblank - * disabled for all CRTCs. - */ - ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1); - if (ret < 0) { - dev_err(&pdev->dev, "failed to initialize vblank\n"); - goto done; - } - - /* DRM/KMS objects */ - ret = rcar_du_modeset_init(rcdu); - if (ret < 0) { - dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret); - goto done; - } - - dev->irq_enabled = 1; - - platform_set_drvdata(pdev, rcdu); - -done: - if (ret) - rcar_du_unload(dev); - - return ret; -} - -static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file) -{ - struct rcar_du_device *rcdu = dev->dev_private; - unsigned int i; - - for (i = 0; i < rcdu->num_crtcs; ++i) - rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file); -} - static void rcar_du_lastclose(struct drm_device *dev) { struct rcar_du_device *rcdu = dev->dev_private; @@ -269,11 +184,7 @@ static const struct file_operations rcar_du_fops = { static struct drm_driver rcar_du_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, - .load = rcar_du_load, - .unload = rcar_du_unload, - .preclose = rcar_du_preclose, .lastclose = rcar_du_lastclose, - .set_busid = drm_platform_set_busid, .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = rcar_du_enable_vblank, .disable_vblank = rcar_du_disable_vblank, @@ -333,18 +244,104 @@ static const struct dev_pm_ops rcar_du_pm_ops = { * Platform driver */ -static int rcar_du_probe(struct platform_device *pdev) +static int rcar_du_remove(struct platform_device *pdev) { - return drm_platform_init(&rcar_du_driver, pdev); + struct rcar_du_device *rcdu = platform_get_drvdata(pdev); + struct drm_device *ddev = rcdu->ddev; + + mutex_lock(&ddev->mode_config.mutex); + drm_connector_unplug_all(ddev); + mutex_unlock(&ddev->mode_config.mutex); + + drm_dev_unregister(ddev); + + if (rcdu->fbdev) + drm_fbdev_cma_fini(rcdu->fbdev); + + drm_kms_helper_poll_fini(ddev); + drm_mode_config_cleanup(ddev); + + drm_dev_unref(ddev); + + return 0; } -static int rcar_du_remove(struct platform_device *pdev) +static int rcar_du_probe(struct platform_device *pdev) { - struct rcar_du_device *rcdu = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + struct rcar_du_device *rcdu; + struct drm_connector *connector; + struct drm_device *ddev; + struct resource *mem; + int ret; + + if (np == NULL) { + dev_err(&pdev->dev, "no device tree node\n"); + return -ENODEV; + } + + /* Allocate and initialize the DRM and R-Car device structures. */ + rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL); + if (rcdu == NULL) + return -ENOMEM; + + init_waitqueue_head(&rcdu->commit.wait); + + rcdu->dev = &pdev->dev; + rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data; + + platform_set_drvdata(pdev, rcdu); - drm_put_dev(rcdu->ddev); + /* I/O resources */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(rcdu->mmio)) + ret = PTR_ERR(rcdu->mmio); + + /* DRM/KMS objects */ + ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev); + if (!ddev) + return -ENOMEM; + + drm_dev_set_unique(ddev, dev_name(&pdev->dev)); + + rcdu->ddev = ddev; + ddev->dev_private = rcdu; + + ret = rcar_du_modeset_init(rcdu); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret); + goto error; + } + + ddev->irq_enabled = 1; + + /* Register the DRM device with the core and the connectors with + * sysfs. + */ + ret = drm_dev_register(ddev, 0); + if (ret) + goto error; + + mutex_lock(&ddev->mode_config.mutex); + drm_for_each_connector(connector, ddev) { + ret = drm_connector_register(connector); + if (ret < 0) + break; + } + mutex_unlock(&ddev->mode_config.mutex); + + if (ret < 0) + goto error; + + DRM_INFO("Device %s probed\n", dev_name(&pdev->dev)); return 0; + +error: + rcar_du_remove(pdev); + + return ret; } static struct platform_driver rcar_du_platform_driver = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c index 96f2eb43713c..6038be93c58d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c @@ -55,12 +55,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { .best_encoder = rcar_du_connector_best_encoder, }; -static void rcar_du_hdmi_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static enum drm_connector_status rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force) { @@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = rcar_du_hdmi_connector_destroy, + .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; @@ -108,9 +102,6 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_connector_register(connector); - if (ret < 0) - return ret; connector->dpms = DRM_MODE_DPMS_OFF; drm_object_property_set_value(&connector->base, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index ca12e8ca5552..46429c4be8e5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -761,6 +761,13 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) if (ret < 0) return ret; + /* Initialize vertical blanking interrupts handling. Start with vblank + * disabled for all CRTCs. + */ + ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1); + if (ret < 0) + return ret; + /* Initialize the groups. */ num_groups = DIV_ROUND_UP(rcdu->num_crtcs, 2); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 0c43032fc693..e905f5da7aaa 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -62,12 +62,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { .best_encoder = rcar_du_connector_best_encoder, }; -static void rcar_du_lvds_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static enum drm_connector_status rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) { @@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_lvds_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = rcar_du_lvds_connector_destroy, + .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; @@ -117,9 +111,6 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_connector_register(connector); - if (ret < 0) - return ret; connector->dpms = DRM_MODE_DPMS_OFF; drm_object_property_set_value(&connector->base, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index e0a5d8f93963..9d7e5c99caf6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -31,12 +31,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { .best_encoder = rcar_du_connector_best_encoder, }; -static void rcar_du_vga_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static enum drm_connector_status rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) { @@ -48,7 +42,7 @@ static const struct drm_connector_funcs connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_vga_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = rcar_du_vga_connector_destroy, + .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; @@ -76,9 +70,6 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_connector_register(connector); - if (ret < 0) - return ret; connector->dpms = DRM_MODE_DPMS_OFF; drm_object_property_set_value(&connector->base, diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index f300eba95bb1..1244cdf52859 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -81,8 +81,10 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, return -ENOMEM; size = roundup(size, PAGE_SIZE); ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size); - if (ret != 0) + if (ret != 0) { + kfree(bo); return ret; + } bo->dumb = false; virtio_gpu_init_ttm_placement(bo, pinned); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index ecf15cf0c3fd..04fd0f2b6af0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -471,7 +471,7 @@ static int vmw_cmd_invalid(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGA3dCmdHeader *header) { - return capable(CAP_SYS_ADMIN) ? : -EINVAL; + return -EINVAL; } static int vmw_cmd_ok(struct vmw_private *dev_priv, diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 6521ec01413e..7cab049771de 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -902,6 +902,9 @@ static int adreno_of_get_power(struct adreno_device *adreno_dev, device->pwrctrl.bus_control = of_property_read_bool(node, "qcom,bus-control"); + device->pwrctrl.input_disable = of_property_read_bool(node, + "qcom,disable-wake-on-touch"); + return 0; } @@ -1016,15 +1019,19 @@ static int adreno_probe(struct platform_device *pdev) /* Initialize coresight for the target */ adreno_coresight_init(adreno_dev); - adreno_input_handler.private = device; - #ifdef CONFIG_INPUT - /* - * It isn't fatal if we cannot register the input handler. Sad, - * perhaps, but not fatal - */ - if (input_register_handler(&adreno_input_handler)) - KGSL_DRV_ERR(device, "Unable to register the input handler\n"); + if (!device->pwrctrl.input_disable) { + adreno_input_handler.private = device; + /* + * It isn't fatal if we cannot register the input handler. Sad, + * perhaps, but not fatal + */ + if (input_register_handler(&adreno_input_handler)) { + adreno_input_handler.private = NULL; + KGSL_DRV_ERR(device, + "Unable to register the input handler\n"); + } + } #endif out: if (status) { @@ -1076,7 +1083,8 @@ static int adreno_remove(struct platform_device *pdev) _adreno_free_memories(adreno_dev); #ifdef CONFIG_INPUT - input_unregister_handler(&adreno_input_handler); + if (adreno_input_handler.private) + input_unregister_handler(&adreno_input_handler); #endif adreno_sysfs_close(adreno_dev); @@ -1309,6 +1317,10 @@ static int _adreno_start(struct adreno_device *adreno_dev) /* make sure ADRENO_DEVICE_STARTED is not set here */ BUG_ON(test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv)); + /* disallow l2pc during wake up to improve GPU wake up time */ + kgsl_pwrctrl_update_l2pc(&adreno_dev->dev, + KGSL_L2PC_WAKEUP_TIMEOUT); + pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma, pmqos_wakeup_vote); diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 218d08e6dfc3..4a0acdcf8844 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -568,6 +568,8 @@ enum adreno_regs { ADRENO_REG_RBBM_RBBM_CTL, ADRENO_REG_UCHE_INVALIDATE0, ADRENO_REG_UCHE_INVALIDATE1, + ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO, + ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO, ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI, ADRENO_REG_RBBM_SECVID_TRUST_CONTROL, @@ -1508,21 +1510,60 @@ static inline void adreno_ringbuffer_set_pagetable(struct adreno_ringbuffer *rb, spin_unlock_irqrestore(&rb->preempt_lock, flags); } +static inline bool is_power_counter_overflow(struct adreno_device *adreno_dev, + unsigned int reg, unsigned int prev_val, unsigned int *perfctr_pwr_hi) +{ + unsigned int val; + bool ret = false; + + /* + * If prev_val is zero, it is first read after perf counter reset. + * So set perfctr_pwr_hi register to zero. + */ + if (prev_val == 0) { + *perfctr_pwr_hi = 0; + return ret; + } + adreno_readreg(adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, &val); + if (val != *perfctr_pwr_hi) { + *perfctr_pwr_hi = val; + ret = true; + } + return ret; +} + static inline unsigned int counter_delta(struct kgsl_device *device, unsigned int reg, unsigned int *counter) { + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); unsigned int val; unsigned int ret = 0; + bool overflow = true; + static unsigned int perfctr_pwr_hi; /* Read the value */ kgsl_regread(device, reg, &val); + if (adreno_is_a5xx(adreno_dev) && reg == adreno_getreg + (adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO)) + overflow = is_power_counter_overflow(adreno_dev, reg, + *counter, &perfctr_pwr_hi); + /* Return 0 for the first read */ if (*counter != 0) { - if (val < *counter) - ret = (0xFFFFFFFF - *counter) + val; - else + if (val >= *counter) { ret = val - *counter; + } else if (overflow == true) { + ret = (0xFFFFFFFF - *counter) + val; + } else { + /* + * Since KGSL got abnormal value from the counter, + * We will drop the value from being accumulated. + */ + pr_warn_once("KGSL: Abnormal value :0x%x (0x%x) from perf counter : 0x%x\n", + val, *counter, reg); + return 0; + } } *counter = val; diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c index 423071811b43..0e3e5b64bdc7 100644 --- a/drivers/gpu/msm/adreno_a3xx.c +++ b/drivers/gpu/msm/adreno_a3xx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1530,6 +1530,10 @@ static unsigned int a3xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { A3XX_UCHE_CACHE_INVALIDATE0_REG), ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE1, A3XX_UCHE_CACHE_INVALIDATE1_REG), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO, + A3XX_RBBM_PERFCTR_RBBM_0_LO), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, + A3XX_RBBM_PERFCTR_RBBM_0_HI), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO, A3XX_RBBM_PERFCTR_LOAD_VALUE_LO), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI, diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c index 5ca04e522270..6170cc263e4a 100644 --- a/drivers/gpu/msm/adreno_a4xx.c +++ b/drivers/gpu/msm/adreno_a4xx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -806,6 +806,10 @@ static unsigned int a4xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SW_RESET_CMD, A4XX_RBBM_SW_RESET_CMD), ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0, A4XX_UCHE_INVALIDATE0), ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE1, A4XX_UCHE_INVALIDATE1), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO, + A4XX_RBBM_PERFCTR_RBBM_0_LO), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, + A4XX_RBBM_PERFCTR_RBBM_0_HI), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO, A4XX_RBBM_PERFCTR_LOAD_VALUE_LO), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI, diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 0715022be6e3..3fb13c7a0814 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -715,6 +715,10 @@ static int _load_gpmu_firmware(struct adreno_device *adreno_dev) if (ret) goto err; + /* Integer overflow check for cmd_size */ + if (data[2] > (data[0] - 2)) + goto err; + cmds = data + data[2] + 3; cmd_size = data[0] - data[2] - 2; @@ -2069,6 +2073,9 @@ static void a5xx_start(struct adreno_device *adreno_dev) } + /* Disable All flat shading optimization */ + kgsl_regrmw(device, A5XX_VPC_DBG_ECO_CNTL, 0, 0x1 << 10); + /* * VPC corner case with local memory load kill leads to corrupt * internal state. Normal Disable does not work for all a5x chips. @@ -3069,6 +3076,10 @@ static unsigned int a5xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_RBBM_BLOCK_SW_RESET_CMD2, A5XX_RBBM_BLOCK_SW_RESET_CMD2), ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0, A5XX_UCHE_INVALIDATE0), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO, + A5XX_RBBM_PERFCTR_RBBM_0_LO), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, + A5XX_RBBM_PERFCTR_RBBM_0_HI), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO, A5XX_RBBM_PERFCTR_LOAD_VALUE_LO), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI, diff --git a/drivers/gpu/msm/adreno_a5xx_preempt.c b/drivers/gpu/msm/adreno_a5xx_preempt.c index 0e56731b16e2..883a9810fbf4 100644 --- a/drivers/gpu/msm/adreno_a5xx_preempt.c +++ b/drivers/gpu/msm/adreno_a5xx_preempt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -537,13 +537,42 @@ static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED, "smmu_info"); } + +static void a5xx_preemption_iommu_close(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); + + kgsl_free_global(device, &iommu->smmu_info); +} + #else static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) { return -ENODEV; } + +static void a5xx_preemption_iommu_close(struct adreno_device *adreno_dev) +{ +} #endif +static void a5xx_preemption_close(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_preemption *preempt = &adreno_dev->preempt; + struct adreno_ringbuffer *rb; + unsigned int i; + + del_timer(&preempt->timer); + kgsl_free_global(device, &preempt->counters); + a5xx_preemption_iommu_close(adreno_dev); + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + kgsl_free_global(device, &rb->preemption_desc); + } +} + int a5xx_preemption_init(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); @@ -568,7 +597,7 @@ int a5xx_preemption_init(struct adreno_device *adreno_dev) A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE, 0, 0, "preemption_counters"); if (ret) - return ret; + goto err; addr = preempt->counters.gpuaddr; @@ -576,10 +605,16 @@ int a5xx_preemption_init(struct adreno_device *adreno_dev) FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { ret = a5xx_preemption_ringbuffer_init(adreno_dev, rb, addr); if (ret) - return ret; + goto err; addr += A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE; } - return a5xx_preemption_iommu_init(adreno_dev); + ret = a5xx_preemption_iommu_init(adreno_dev); + +err: + if (ret) + a5xx_preemption_close(device); + + return ret; } diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index 5306303b8d15..2027ac66f737 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -131,6 +131,8 @@ typedef void (*reg_read_fill_t)(struct kgsl_device *device, int i, static void sync_event_print(struct seq_file *s, struct kgsl_drawobj_sync_event *sync_event) { + unsigned long flags; + switch (sync_event->type) { case KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP: { seq_printf(s, "sync: ctx: %d ts: %d", @@ -138,9 +140,13 @@ static void sync_event_print(struct seq_file *s, break; } case KGSL_CMD_SYNCPOINT_TYPE_FENCE: + spin_lock_irqsave(&sync_event->handle_lock, flags); + seq_printf(s, "sync: [%pK] %s", sync_event->handle, (sync_event->handle && sync_event->handle->fence) ? sync_event->handle->fence->name : "NULL"); + + spin_unlock_irqrestore(&sync_event->handle_lock, flags); break; default: seq_printf(s, "sync: type: %d", sync_event->type); diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index 8902c3175c79..1a94e71f5c1d 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -1460,7 +1460,9 @@ int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv, spin_unlock(&drawctxt->lock); - kgsl_pwrctrl_update_l2pc(&adreno_dev->dev); + if (device->pwrctrl.l2pc_update_queue) + kgsl_pwrctrl_update_l2pc(&adreno_dev->dev, + KGSL_L2PC_QUEUE_TIMEOUT); /* Add the context to the dispatcher pending list */ dispatcher_queue_context(adreno_dev, drawctxt); diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index d79d9613043f..ddc53edce3c1 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -26,6 +26,7 @@ #include "adreno_iommu.h" #include "adreno_pm4types.h" #include "adreno_ringbuffer.h" +#include "adreno_trace.h" #include "a3xx_reg.h" #include "adreno_a5xx.h" @@ -58,6 +59,7 @@ static void _cff_write_ringbuffer(struct adreno_ringbuffer *rb) } static void adreno_get_submit_time(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, struct adreno_submit_time *time) { unsigned long flags; @@ -87,6 +89,9 @@ static void adreno_get_submit_time(struct adreno_device *adreno_dev, } else time->ticks = 0; + /* Trace the GPU time to create a mapping to ftrace time */ + trace_adreno_cmdbatch_sync(rb->drawctxt_active, time->ticks); + /* Get the kernel clock for time since boot */ time->ktime = local_clock(); @@ -128,7 +133,7 @@ void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb, _cff_write_ringbuffer(rb); if (time != NULL) - adreno_get_submit_time(adreno_dev, time); + adreno_get_submit_time(adreno_dev, rb, time); adreno_ringbuffer_wptr(adreno_dev, rb); } diff --git a/drivers/gpu/msm/adreno_trace.h b/drivers/gpu/msm/adreno_trace.h index 16ca0980cfbe..74c4c4e6e1fa 100644 --- a/drivers/gpu/msm/adreno_trace.h +++ b/drivers/gpu/msm/adreno_trace.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -148,6 +148,29 @@ TRACE_EVENT(adreno_cmdbatch_retired, ) ); +TRACE_EVENT(adreno_cmdbatch_sync, + TP_PROTO(struct adreno_context *drawctxt, + uint64_t ticks), + TP_ARGS(drawctxt, ticks), + TP_STRUCT__entry( + __field(unsigned int, id) + __field(unsigned int, timestamp) + __field(uint64_t, ticks) + __field(int, prio) + ), + TP_fast_assign( + __entry->id = drawctxt->base.id; + __entry->timestamp = drawctxt->timestamp; + __entry->ticks = ticks; + __entry->prio = drawctxt->base.priority; + ), + TP_printk( + "ctx=%u ctx_prio=%d ts=%u ticks=%lld", + __entry->id, __entry->prio, __entry->timestamp, + __entry->ticks + ) +); + TRACE_EVENT(adreno_cmdbatch_fault, TP_PROTO(struct kgsl_drawobj_cmd *cmdobj, unsigned int fault), TP_ARGS(cmdobj, fault), diff --git a/drivers/gpu/msm/kgsl_drawobj.c b/drivers/gpu/msm/kgsl_drawobj.c index f8f0e7ccb0d3..fba18231cb72 100644 --- a/drivers/gpu/msm/kgsl_drawobj.c +++ b/drivers/gpu/msm/kgsl_drawobj.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -79,6 +79,7 @@ void kgsl_dump_syncpoints(struct kgsl_device *device, { struct kgsl_drawobj_sync_event *event; unsigned int i; + unsigned long flags; for (i = 0; i < syncobj->numsyncs; i++) { event = &syncobj->synclist[i]; @@ -101,12 +102,16 @@ void kgsl_dump_syncpoints(struct kgsl_device *device, break; } case KGSL_CMD_SYNCPOINT_TYPE_FENCE: + spin_lock_irqsave(&event->handle_lock, flags); + if (event->handle) dev_err(device->dev, " fence: [%pK] %s\n", event->handle->fence, event->handle->name); else dev_err(device->dev, " fence: invalid\n"); + + spin_unlock_irqrestore(&event->handle_lock, flags); break; } } @@ -119,6 +124,7 @@ static void syncobj_timer(unsigned long data) struct kgsl_drawobj *drawobj = DRAWOBJ(syncobj); struct kgsl_drawobj_sync_event *event; unsigned int i; + unsigned long flags; if (syncobj == NULL || drawobj->context == NULL) return; @@ -147,12 +153,16 @@ static void syncobj_timer(unsigned long data) i, event->context->id, event->timestamp); break; case KGSL_CMD_SYNCPOINT_TYPE_FENCE: + spin_lock_irqsave(&event->handle_lock, flags); + if (event->handle != NULL) { dev_err(device->dev, " [%d] FENCE %s\n", i, event->handle->fence ? event->handle->fence->name : "NULL"); kgsl_sync_fence_log(event->handle->fence); } + + spin_unlock_irqrestore(&event->handle_lock, flags); break; } } @@ -231,7 +241,7 @@ static void drawobj_destroy_sparse(struct kgsl_drawobj *drawobj) static void drawobj_destroy_sync(struct kgsl_drawobj *drawobj) { struct kgsl_drawobj_sync *syncobj = SYNCOBJ(drawobj); - unsigned long pending; + unsigned long pending, flags; unsigned int i; /* Zap the canary timer */ @@ -262,8 +272,17 @@ static void drawobj_destroy_sync(struct kgsl_drawobj *drawobj) drawobj_sync_func, event); break; case KGSL_CMD_SYNCPOINT_TYPE_FENCE: - if (kgsl_sync_fence_async_cancel(event->handle)) + spin_lock_irqsave(&event->handle_lock, flags); + + if (kgsl_sync_fence_async_cancel(event->handle)) { + event->handle = NULL; + spin_unlock_irqrestore( + &event->handle_lock, flags); drawobj_put(drawobj); + } else { + spin_unlock_irqrestore( + &event->handle_lock, flags); + } break; } } @@ -325,12 +344,23 @@ EXPORT_SYMBOL(kgsl_drawobj_destroy); static void drawobj_sync_fence_func(void *priv) { + unsigned long flags; struct kgsl_drawobj_sync_event *event = priv; + drawobj_sync_expire(event->device, event); + trace_syncpoint_fence_expire(event->syncobj, event->handle ? event->handle->name : "unknown"); - drawobj_sync_expire(event->device, event); + spin_lock_irqsave(&event->handle_lock, flags); + + /* + * Setting the event->handle to NULL here make sure that + * other function does not dereference a invalid pointer. + */ + event->handle = NULL; + + spin_unlock_irqrestore(&event->handle_lock, flags); drawobj_put(&event->syncobj->base); } @@ -348,7 +378,14 @@ static int drawobj_add_sync_fence(struct kgsl_device *device, struct kgsl_cmd_syncpoint_fence *sync = priv; struct kgsl_drawobj *drawobj = DRAWOBJ(syncobj); struct kgsl_drawobj_sync_event *event; + struct sync_fence *fence = NULL; unsigned int id; + unsigned long flags; + int ret = 0; + + fence = sync_fence_fdget(sync->fd); + if (fence == NULL) + return -EINVAL; kref_get(&drawobj->refcount); @@ -362,32 +399,39 @@ static int drawobj_add_sync_fence(struct kgsl_device *device, event->device = device; event->context = NULL; + spin_lock_init(&event->handle_lock); set_bit(event->id, &syncobj->pending); + trace_syncpoint_fence(syncobj, fence->name); + + spin_lock_irqsave(&event->handle_lock, flags); + event->handle = kgsl_sync_fence_async_wait(sync->fd, drawobj_sync_fence_func, event); if (IS_ERR_OR_NULL(event->handle)) { - int ret = PTR_ERR(event->handle); + ret = PTR_ERR(event->handle); - clear_bit(event->id, &syncobj->pending); event->handle = NULL; + spin_unlock_irqrestore(&event->handle_lock, flags); + + clear_bit(event->id, &syncobj->pending); drawobj_put(drawobj); /* - * If ret == 0 the fence was already signaled - print a trace - * message so we can track that + * Print a syncpoint_fence_expire trace if + * the fence is already signaled or there is + * a failure in registering the fence waiter. */ - if (ret == 0) - trace_syncpoint_fence_expire(syncobj, "signaled"); - - return ret; + trace_syncpoint_fence_expire(syncobj, (ret < 0) ? + "error" : fence->name); + } else { + spin_unlock_irqrestore(&event->handle_lock, flags); } - trace_syncpoint_fence(syncobj, event->handle->name); - - return 0; + sync_fence_put(fence); + return ret; } /* drawobj_add_sync_timestamp() - Add a new sync point for a sync obj diff --git a/drivers/gpu/msm/kgsl_drawobj.h b/drivers/gpu/msm/kgsl_drawobj.h index fd9d2bc93f41..b208870e4c42 100644 --- a/drivers/gpu/msm/kgsl_drawobj.h +++ b/drivers/gpu/msm/kgsl_drawobj.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -114,6 +114,7 @@ struct kgsl_drawobj_sync { * register this event * @timestamp: Pending timestamp for the event * @handle: Pointer to a sync fence handle + * @handle_lock: Spin lock to protect handle * @device: Pointer to the KGSL device */ struct kgsl_drawobj_sync_event { @@ -123,6 +124,7 @@ struct kgsl_drawobj_sync_event { struct kgsl_context *context; unsigned int timestamp; struct kgsl_sync_fence_waiter *handle; + spinlock_t handle_lock; struct kgsl_device *device; }; diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c index c31a85b07447..685ce3ea968b 100644 --- a/drivers/gpu/msm/kgsl_pool.c +++ b/drivers/gpu/msm/kgsl_pool.c @@ -65,26 +65,19 @@ _kgsl_get_pool_from_order(unsigned int order) /* Map the page into kernel and zero it out */ static void -_kgsl_pool_zero_page(struct page *p, unsigned int pool_order) +_kgsl_pool_zero_page(struct page *p) { - int i; - - for (i = 0; i < (1 << pool_order); i++) { - struct page *page = nth_page(p, i); - void *addr = kmap_atomic(page); + void *addr = kmap_atomic(p); - memset(addr, 0, PAGE_SIZE); - dmac_flush_range(addr, addr + PAGE_SIZE); - kunmap_atomic(addr); - } + memset(addr, 0, PAGE_SIZE); + dmac_flush_range(addr, addr + PAGE_SIZE); + kunmap_atomic(addr); } /* Add a page to specified pool */ static void _kgsl_pool_add_page(struct kgsl_page_pool *pool, struct page *p) { - _kgsl_pool_zero_page(p, pool->pool_order); - spin_lock(&pool->list_lock); list_add_tail(&p->lru, &pool->page_list); pool->page_count++; @@ -329,7 +322,6 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages, } else return -ENOMEM; } - _kgsl_pool_zero_page(page, order); goto done; } @@ -349,7 +341,6 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages, page = alloc_pages(gfp_mask, order); if (page == NULL) return -ENOMEM; - _kgsl_pool_zero_page(page, order); goto done; } } @@ -379,13 +370,12 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages, } else return -ENOMEM; } - - _kgsl_pool_zero_page(page, order); } done: for (j = 0; j < (*page_size >> PAGE_SHIFT); j++) { p = nth_page(page, j); + _kgsl_pool_zero_page(p); pages[pcount] = p; pcount++; } diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index ad8b0131bb46..8c998a5d791b 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -43,13 +43,6 @@ #define DEFAULT_BUS_P 25 -/* - * The effective duration of qos request in usecs. After - * timeout, qos request is cancelled automatically. - * Kept 80ms default, inline with default GPU idle time. - */ -#define KGSL_L2PC_CPU_TIMEOUT (80 * 1000) - /* Order deeply matters here because reasons. New entries go on the end */ static const char * const clocks[] = { "src_clk", @@ -520,12 +513,14 @@ EXPORT_SYMBOL(kgsl_pwrctrl_set_constraint); /** * kgsl_pwrctrl_update_l2pc() - Update existing qos request * @device: Pointer to the kgsl_device struct + * @timeout_us: the effective duration of qos request in usecs. * * Updates an existing qos request to avoid L2PC on the * CPUs (which are selected through dtsi) on which GPU * thread is running. This would help for performance. */ -void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device) +void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device, + unsigned long timeout_us) { int cpu; @@ -539,7 +534,7 @@ void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device) pm_qos_update_request_timeout( &device->pwrctrl.l2pc_cpus_qos, device->pwrctrl.pm_qos_cpu_mask_latency, - KGSL_L2PC_CPU_TIMEOUT); + timeout_us); } } EXPORT_SYMBOL(kgsl_pwrctrl_update_l2pc); @@ -2201,6 +2196,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) kgsl_property_read_u32(device, "qcom,l2pc-cpu-mask", &pwr->l2pc_cpus_mask); + pwr->l2pc_update_queue = of_property_read_bool( + device->pdev->dev.of_node, + "qcom,l2pc-update-queue"); + pm_runtime_enable(&pdev->dev); ocmem_bus_node = of_find_node_by_name( diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 42f918b80fcd..02707c901839 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -51,6 +51,19 @@ #define KGSL_PWR_DEL_LIMIT 1 #define KGSL_PWR_SET_LIMIT 2 +/* + * The effective duration of qos request in usecs at queue time. + * After timeout, qos request is cancelled automatically. + * Kept 80ms default, inline with default GPU idle time. + */ +#define KGSL_L2PC_QUEUE_TIMEOUT (80 * 1000) + +/* + * The effective duration of qos request in usecs at wakeup time. + * After timeout, qos request is cancelled automatically. + */ +#define KGSL_L2PC_WAKEUP_TIMEOUT (10 * 1000) + enum kgsl_pwrctrl_timer_type { KGSL_PWR_IDLE_TIMER, }; @@ -128,10 +141,12 @@ struct kgsl_regulator { * @irq_name - resource name for the IRQ * @clk_stats - structure of clock statistics * @l2pc_cpus_mask - mask to avoid L2PC on masked CPUs + * @l2pc_update_queue - Boolean flag to avoid L2PC on masked CPUs at queue time * @l2pc_cpus_qos - qos structure to avoid L2PC on CPUs * @pm_qos_req_dma - the power management quality of service structure * @pm_qos_active_latency - allowed CPU latency in microseconds when active * @pm_qos_cpu_mask_latency - allowed CPU mask latency in microseconds + * @input_disable - To disable GPU wakeup on touch input event * @pm_qos_wakeup_latency - allowed CPU latency in microseconds during wakeup * @bus_control - true if the bus calculation is independent * @bus_mod - modifier from the current power level for the bus vote @@ -183,11 +198,13 @@ struct kgsl_pwrctrl { const char *irq_name; struct kgsl_clk_stats clk_stats; unsigned int l2pc_cpus_mask; + bool l2pc_update_queue; struct pm_qos_request l2pc_cpus_qos; struct pm_qos_request pm_qos_req_dma; unsigned int pm_qos_active_latency; unsigned int pm_qos_cpu_mask_latency; unsigned int pm_qos_wakeup_latency; + bool input_disable; bool bus_control; int bus_mod; unsigned int bus_percent_ab; @@ -249,5 +266,6 @@ int kgsl_active_count_wait(struct kgsl_device *device, int count); void kgsl_pwrctrl_busy_time(struct kgsl_device *device, u64 time, u64 busy); void kgsl_pwrctrl_set_constraint(struct kgsl_device *device, struct kgsl_pwr_constraint *pwrc, uint32_t id); -void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device); +void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device, + unsigned long timeout_us); #endif /* __KGSL_PWRCTRL_H */ diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index 3e4cc4490792..2b7a1fcbaa78 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -591,7 +591,7 @@ int kgsl_devfreq_get_dev_status(struct device *dev, struct kgsl_device *device = dev_get_drvdata(dev); struct kgsl_pwrctrl *pwrctrl; struct kgsl_pwrscale *pwrscale; - ktime_t tmp; + ktime_t tmp1, tmp2; if (device == NULL) return -ENODEV; @@ -602,6 +602,8 @@ int kgsl_devfreq_get_dev_status(struct device *dev, pwrctrl = &device->pwrctrl; mutex_lock(&device->mutex); + + tmp1 = ktime_get(); /* * If the GPU clock is on grab the latest power counter * values. Otherwise the most recent ACTIVE values will @@ -609,9 +611,9 @@ int kgsl_devfreq_get_dev_status(struct device *dev, */ kgsl_pwrscale_update_stats(device); - tmp = ktime_get(); - stat->total_time = ktime_us_delta(tmp, pwrscale->time); - pwrscale->time = tmp; + tmp2 = ktime_get(); + stat->total_time = ktime_us_delta(tmp2, pwrscale->time); + pwrscale->time = tmp1; stat->busy_time = pwrscale->accum_stats.busy_time; diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c index 358b3b038899..4bf591c236a7 100644 --- a/drivers/gpu/msm/kgsl_sync.c +++ b/drivers/gpu/msm/kgsl_sync.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -179,7 +179,7 @@ int kgsl_add_fence_event(struct kgsl_device *device, goto out; } snprintf(fence_name, sizeof(fence_name), - "%s-pid-%d-ctx-%d-ts-%d", + "%s-pid-%d-ctx-%d-ts-%u", device->name, current->group_leader->pid, context_id, timestamp); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 34ea83d067af..13dc2731195b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2428,6 +2428,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PETZL, USB_DEVICE_ID_PETZL_HEADLAMP) }, { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, #if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c7f8b70d15ee..37cbc2ecfc5f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -777,6 +777,9 @@ #define USB_VENDOR_ID_PETALYNX 0x18b1 #define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 +#define USB_VENDOR_ID_PETZL 0x2122 +#define USB_DEVICE_ID_PETZL_HEADLAMP 0x1234 + #define USB_VENDOR_ID_PHILIPS 0x0471 #define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617 diff --git a/drivers/hwtracing/coresight/coresight-remote-etm.c b/drivers/hwtracing/coresight/coresight-remote-etm.c index 30b13282f6c0..cc0b25b130d7 100644 --- a/drivers/hwtracing/coresight/coresight-remote-etm.c +++ b/drivers/hwtracing/coresight/coresight-remote-etm.c @@ -186,12 +186,9 @@ static void remote_etm_rcv_msg(struct work_struct *work) struct remote_etm_drvdata *drvdata = container_of(work, struct remote_etm_drvdata, work_rcv_msg); - mutex_lock(&drvdata->mutex); if (qmi_recv_msg(drvdata->handle) < 0) dev_err(drvdata->dev, "%s: Error receiving QMI message\n", __func__); - - mutex_unlock(&drvdata->mutex); } static void remote_etm_notify(struct qmi_handle *handle, diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index 5c20970ccbbb..28ab4e52dab5 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -180,6 +180,9 @@ #define FG_ADC_RR_VOLT_INPUT_FACTOR 8 #define FG_ADC_RR_CURR_INPUT_FACTOR 2000 #define FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL 1886 +#define FG_ADC_RR_CURR_USBIN_660_FACTOR_MIL 9 +#define FG_ADC_RR_CURR_USBIN_660_UV_VAL 579500 + #define FG_ADC_SCALE_MILLI_FACTOR 1000 #define FG_ADC_KELVINMIL_CELSIUSMIL 273150 @@ -192,6 +195,9 @@ #define FG_RR_CONV_CONTINUOUS_TIME_MIN_US 50000 #define FG_RR_CONV_CONTINUOUS_TIME_MAX_US 51000 #define FG_RR_CONV_MAX_RETRY_CNT 50 +#define FG_RR_TP_REV_VERSION1 21 +#define FG_RR_TP_REV_VERSION2 29 +#define FG_RR_TP_REV_VERSION3 32 /* * The channel number is not a physical index in hardware, @@ -228,6 +234,7 @@ struct rradc_chip { struct rradc_chan_prop *chan_props; struct device_node *revid_dev_node; struct pmic_revid_data *pmic_fab_id; + int volt; }; struct rradc_channels { @@ -353,7 +360,7 @@ static int rradc_post_process_volt(struct rradc_chip *chip, return 0; } -static int rradc_post_process_curr(struct rradc_chip *chip, +static int rradc_post_process_usbin_curr(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, int *result_ua) { @@ -361,11 +368,33 @@ static int rradc_post_process_curr(struct rradc_chip *chip, if (!prop) return -EINVAL; - - if (prop->channel == RR_ADC_USBIN_I) - scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL; - else - scale = FG_ADC_RR_CURR_INPUT_FACTOR; + if (chip->revid_dev_node) { + switch (chip->pmic_fab_id->pmic_subtype) { + case PM660_SUBTYPE: + if (((chip->pmic_fab_id->tp_rev + >= FG_RR_TP_REV_VERSION1) + && (chip->pmic_fab_id->tp_rev + <= FG_RR_TP_REV_VERSION2)) + || (chip->pmic_fab_id->tp_rev + >= FG_RR_TP_REV_VERSION3)) { + chip->volt = div64_s64(chip->volt, 1000); + chip->volt = chip->volt * + FG_ADC_RR_CURR_USBIN_660_FACTOR_MIL; + chip->volt = FG_ADC_RR_CURR_USBIN_660_UV_VAL - + (chip->volt); + chip->volt = div64_s64(1000000000, chip->volt); + scale = chip->volt; + } else + scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL; + break; + case PMI8998_SUBTYPE: + scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL; + break; + default: + pr_err("No PMIC subtype found\n"); + return -EINVAL; + } + } /* scale * V/A; 2.5V ADC full scale */ ua = ((int64_t)adc_code * scale); @@ -376,6 +405,24 @@ static int rradc_post_process_curr(struct rradc_chip *chip, return 0; } +static int rradc_post_process_dcin_curr(struct rradc_chip *chip, + struct rradc_chan_prop *prop, u16 adc_code, + int *result_ua) +{ + int64_t ua = 0; + + if (!prop) + return -EINVAL; + + /* 0.5 V/A; 2.5V ADC full scale */ + ua = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR); + ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR); + ua = div64_s64(ua, (FG_MAX_ADC_READINGS * 1000)); + *result_ua = ua; + + return 0; +} + static int rradc_post_process_die_temp(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, int *result_millidegc) @@ -591,13 +638,13 @@ static const struct rradc_channels rradc_chans[] = { BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB, FG_ADC_RR_AUX_THERM_STS) - RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr, + RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_usbin_curr, FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB, FG_ADC_RR_USB_IN_I_STS) RR_ADC_CHAN_VOLT("usbin_v", &rradc_post_process_volt, FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB, FG_ADC_RR_USB_IN_V_STS) - RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_curr, + RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_dcin_curr, FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB, FG_ADC_RR_DC_IN_I_STS) RR_ADC_CHAN_VOLT("dcin_v", &rradc_post_process_volt, @@ -955,6 +1002,21 @@ static int rradc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_PROCESSED: + if (((chip->pmic_fab_id->tp_rev + >= FG_RR_TP_REV_VERSION1) + && (chip->pmic_fab_id->tp_rev + <= FG_RR_TP_REV_VERSION2)) + || (chip->pmic_fab_id->tp_rev + >= FG_RR_TP_REV_VERSION3)) { + if (chan->address == RR_ADC_USBIN_I) { + prop = &chip->chan_props[RR_ADC_USBIN_V]; + rc = rradc_do_conversion(chip, prop, &adc_code); + if (rc) + break; + prop->scale(chip, prop, adc_code, &chip->volt); + } + } + prop = &chip->chan_props[chan->address]; rc = rradc_do_conversion(chip, prop, &adc_code); if (rc) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 3f5741a3e728..43d5166db4c6 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -857,6 +857,8 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr, } else ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr, qp_attr_mask); + qp_attr->port_num = id_priv->id.port_num; + *qp_attr_mask |= IB_QP_PORT; } else ret = -ENOSYS; diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 1c02deab068f..b7a73f1a8beb 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -2287,6 +2287,11 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file, if (copy_from_user(&cmd, buf, sizeof cmd)) return -EFAULT; + if ((cmd.attr_mask & IB_QP_PORT) && + (cmd.port_num < rdma_start_port(ib_dev) || + cmd.port_num > rdma_end_port(ib_dev))) + return -EINVAL; + INIT_UDATA(&udata, buf + sizeof cmd, NULL, in_len - sizeof cmd, out_len); @@ -2827,6 +2832,10 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file, if (copy_from_user(&cmd, buf, sizeof cmd)) return -EFAULT; + if (cmd.attr.port_num < rdma_start_port(ib_dev) || + cmd.attr.port_num > rdma_end_port(ib_dev)) + return -EINVAL; + uobj = kmalloc(sizeof *uobj, GFP_KERNEL); if (!uobj) return -ENOMEM; diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c index 56f2732334db..f85556079d12 100644 --- a/drivers/input/misc/hbtp_input.c +++ b/drivers/input/misc/hbtp_input.c @@ -98,9 +98,10 @@ static struct hbtp_data *hbtp; static struct kobject *sensor_kobject; #if defined(CONFIG_FB) +static int hbtp_fb_early_suspend(struct hbtp_data *ts); static int hbtp_fb_suspend(struct hbtp_data *ts); static int hbtp_fb_early_resume(struct hbtp_data *ts); -static int hbtp_fb_resume(struct hbtp_data *ts); +static int hbtp_fb_revert_resume(struct hbtp_data *ts); #endif #if defined(CONFIG_FB) @@ -145,6 +146,7 @@ static int fb_notifier_callback(struct notifier_block *self, lcd_state <= FB_BLANK_NORMAL) { pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n", __func__); + hbtp_fb_early_suspend(hbtp_data); } else { pr_debug("%s: receives EARLY_BLANK:%d in %d state\n", __func__, blank, lcd_state); @@ -153,10 +155,12 @@ static int fb_notifier_callback(struct notifier_block *self, if (blank <= FB_BLANK_NORMAL) { pr_debug("%s: receives R_EARLY_BALNK:UNBLANK\n", __func__); + hbtp_fb_early_suspend(hbtp_data); hbtp_fb_suspend(hbtp_data); } else if (blank == FB_BLANK_POWERDOWN) { pr_debug("%s: receives R_EARLY_BALNK:POWERDOWN\n", __func__); + hbtp_fb_revert_resume(hbtp_data); } else { pr_debug("%s: receives R_EARLY_BALNK:%d in %d state\n", __func__, blank, lcd_state); @@ -175,7 +179,6 @@ static int fb_notifier_callback(struct notifier_block *self, } else if (blank <= FB_BLANK_NORMAL && lcd_state == FB_BLANK_POWERDOWN) { pr_debug("%s: receives BLANK:UNBLANK\n", __func__); - hbtp_fb_resume(hbtp_data); } else { pr_debug("%s: receives BLANK:%d in %d state\n", __func__, blank, lcd_state); @@ -1192,6 +1195,43 @@ error: return rc; } +static int hbtp_fb_early_suspend(struct hbtp_data *ts) +{ + int rc = 0; + char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL}; + + mutex_lock(&hbtp->mutex); + if (ts->pdev && (!ts->power_sync_enabled)) { + pr_debug("%s: power_sync is not enabled\n", __func__); + + if (ts->input_dev) { + kobject_uevent_env(&ts->input_dev->dev.kobj, + KOBJ_OFFLINE, envp); + + if (ts->power_sig_enabled) { + pr_debug("%s: power_sig is enabled, wait for signal\n", + __func__); + mutex_unlock(&hbtp->mutex); + rc = wait_for_completion_interruptible( + &hbtp->power_suspend_sig); + if (rc != 0) { + pr_err("%s: wait for early suspend is interrupted\n", + __func__); + } + mutex_lock(&hbtp->mutex); + pr_debug("%s: Wait is done for early suspend\n", + __func__); + } else { + pr_debug("%s: power_sig is NOT enabled", + __func__); + } + } + } + + mutex_unlock(&hbtp->mutex); + return rc; +} + static int hbtp_fb_suspend(struct hbtp_data *ts) { int rc; @@ -1217,26 +1257,28 @@ static int hbtp_fb_suspend(struct hbtp_data *ts) goto err_power_disable; } ts->power_suspended = true; - } - if (ts->input_dev) { - kobject_uevent_env(&ts->input_dev->dev.kobj, - KOBJ_OFFLINE, envp); + if (ts->input_dev) { + kobject_uevent_env(&ts->input_dev->dev.kobj, + KOBJ_OFFLINE, envp); - if (ts->power_sig_enabled) { - pr_debug("%s: power_sig is enabled, wait for signal\n", - __func__); - mutex_unlock(&hbtp->mutex); - rc = wait_for_completion_interruptible( - &hbtp->power_suspend_sig); - if (rc != 0) { - pr_err("%s: wait for suspend is interrupted\n", - __func__); + if (ts->power_sig_enabled) { + pr_debug("%s: power_sig is enabled, wait for signal\n", + __func__); + mutex_unlock(&hbtp->mutex); + rc = wait_for_completion_interruptible( + &hbtp->power_suspend_sig); + if (rc != 0) { + pr_err("%s: wait for suspend is interrupted\n", + __func__); + } + mutex_lock(&hbtp->mutex); + pr_debug("%s: Wait is done for suspend\n", + __func__); + } else { + pr_debug("%s: power_sig is NOT enabled", + __func__); } - mutex_lock(&hbtp->mutex); - pr_debug("%s: Wait is done for suspend\n", __func__); - } else { - pr_debug("%s: power_sig is NOT enabled", __func__); } } @@ -1278,39 +1320,40 @@ static int hbtp_fb_early_resume(struct hbtp_data *ts) goto err_pin_enable; } + if (ts->fb_resume_seq_delay) { + usleep_range(ts->fb_resume_seq_delay, + ts->fb_resume_seq_delay + + HBTP_HOLD_DURATION_US); + pr_debug("%s: fb_resume_seq_delay = %u\n", + __func__, ts->fb_resume_seq_delay); + } + ts->power_suspended = false; + } - if (ts->input_dev) { + if (ts->input_dev) { - kobject_uevent_env(&ts->input_dev->dev.kobj, - KOBJ_ONLINE, envp); + kobject_uevent_env(&ts->input_dev->dev.kobj, + KOBJ_ONLINE, envp); - if (ts->power_sig_enabled) { - pr_err("%s: power_sig is enabled, wait for signal\n", + if (ts->power_sig_enabled) { + pr_err("%s: power_sig is enabled, wait for signal\n", __func__); - mutex_unlock(&hbtp->mutex); - rc = wait_for_completion_interruptible( + mutex_unlock(&hbtp->mutex); + rc = wait_for_completion_interruptible( &hbtp->power_resume_sig); - if (rc != 0) { - pr_err("%s: wait for resume is interrupted\n", + if (rc != 0) { + pr_err("%s: wait for resume is interrupted\n", __func__); - } - mutex_lock(&hbtp->mutex); - pr_debug("%s: wait is done\n", __func__); - } else { - pr_debug("%s: power_sig is NOT enabled\n", - __func__); - } - - if (ts->fb_resume_seq_delay) { - usleep_range(ts->fb_resume_seq_delay, - ts->fb_resume_seq_delay + - HBTP_HOLD_DURATION_US); - pr_err("%s: fb_resume_seq_delay = %u\n", - __func__, ts->fb_resume_seq_delay); } + mutex_lock(&hbtp->mutex); + pr_debug("%s: wait is done\n", __func__); + } else { + pr_debug("%s: power_sig is NOT enabled\n", + __func__); } } + mutex_unlock(&hbtp->mutex); return 0; @@ -1321,20 +1364,41 @@ err_power_on: return rc; } -static int hbtp_fb_resume(struct hbtp_data *ts) +static int hbtp_fb_revert_resume(struct hbtp_data *ts) { char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL}; + int rc = 0; mutex_lock(&hbtp->mutex); - if (!ts->power_sync_enabled) { - pr_debug("%s: power_sync is disabled, send uevent\n", __func__); + + pr_debug("%s: hbtp_fb_revert_resume\n", __func__); + + if (ts->pdev && (!ts->power_sync_enabled)) { + pr_debug("%s: power_sync is not enabled\n", __func__); + if (ts->input_dev) { kobject_uevent_env(&ts->input_dev->dev.kobj, - KOBJ_ONLINE, envp); + KOBJ_ONLINE, envp); + + if (ts->power_sig_enabled) { + pr_debug("%s: power_sig is enabled, wait for signal\n", + __func__); + mutex_unlock(&hbtp->mutex); + rc = wait_for_completion_interruptible( + &hbtp->power_resume_sig); + if (rc != 0) { + pr_warn("%s: wait for revert resume is interrupted\n", + __func__); + } + pr_debug("%s: wait is done\n", __func__); + } else { + pr_debug("%s: power_sig is NOT enabled\n", + __func__); + } } } - mutex_unlock(&hbtp->mutex); - return 0; + + return rc; } static int hbtp_pdev_probe(struct platform_device *pdev) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 89abfdb539ac..c84c685056b9 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -434,8 +434,10 @@ static int i8042_start(struct serio *serio) { struct i8042_port *port = serio->port_data; + spin_lock_irq(&i8042_lock); port->exists = true; - mb(); + spin_unlock_irq(&i8042_lock); + return 0; } @@ -448,16 +450,20 @@ static void i8042_stop(struct serio *serio) { struct i8042_port *port = serio->port_data; + spin_lock_irq(&i8042_lock); port->exists = false; + port->serio = NULL; + spin_unlock_irq(&i8042_lock); /* + * We need to make sure that interrupt handler finishes using + * our serio port before we return from this function. * We synchronize with both AUX and KBD IRQs because there is * a (very unlikely) chance that AUX IRQ is raised for KBD port * and vice versa. */ synchronize_irq(I8042_AUX_IRQ); synchronize_irq(I8042_KBD_IRQ); - port->serio = NULL; } /* @@ -574,7 +580,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) spin_unlock_irqrestore(&i8042_lock, flags); - if (likely(port->exists && !filtered)) + if (likely(serio && !filtered)) serio_interrupt(serio, data, dfl); out: diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index 78bdd24af28b..08bfb83a9447 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -1003,7 +1003,10 @@ static unsigned char *fts_status_event_handler( case FTS_WATER_MODE_ON: case FTS_WATER_MODE_OFF: default: - logError(1, "%s %s Received unhandled status event = %02X %02X %02X %02X %02X %02X %02X %02X\n", tag, __func__, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); + logError(0, + "%s %s Received unhandled status event = %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], + event[3], event[4], event[5], event[6], event[7]); break; } @@ -1755,8 +1758,6 @@ static int fts_fb_state_chg_callback(struct notifier_block *nb, unsigned long va info->resume_bit = 1; - fts_system_reset(); - fts_mode_handler(info, 0); info->sensor_sleep = false; @@ -1959,9 +1960,9 @@ static int parse_dt(struct device *dev, struct fts_i2c_platform_data *bdata) bdata->bus_reg_name = name; logError(0, "%s bus_reg_name = %s\n", tag, name); - if (of_property_read_bool(np, "st, reset-gpio")) { + if (of_property_read_bool(np, "st,reset-gpio")) { bdata->reset_gpio = of_get_named_gpio_flags(np, - "st, reset-gpio", 0, NULL); + "st,reset-gpio", 0, NULL); logError(0, "%s reset_gpio =%d\n", tag, bdata->reset_gpio); } else { bdata->reset_gpio = GPIO_NOT_DEFINED; @@ -2210,7 +2211,13 @@ static int fts_probe(struct i2c_client *client, } #endif - queue_delayed_work(info->fwu_workqueue, &info->fwu_work, msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + /*if wanna auto-update FW when probe, + * please don't comment the following code + */ + + /* queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + * msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + */ logError(1, "%s Probe Finished!\n", tag); return OK; diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index c6f74b149706..ce18a512b76a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1727,7 +1727,8 @@ static void arm_smmu_pgtbl_unlock(struct arm_smmu_domain *smmu_domain, static int arm_smmu_restore_sec_cfg(struct arm_smmu_device *smmu) { - int ret, scm_ret; + int ret; + u64 scm_ret; if (!arm_smmu_is_static_cb(smmu)) return 0; diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 2e0f61a2dc3f..9e96d81bc5cd 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -793,6 +793,9 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, int enabled; u64 val; + if (cpu >= nr_cpu_ids) + return -EINVAL; + if (gic_irq_in_rdist(d)) return -EINVAL; diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c index deb89d63a728..e684be1bb7c0 100644 --- a/drivers/irqchip/irq-keystone.c +++ b/drivers/irqchip/irq-keystone.c @@ -19,9 +19,9 @@ #include <linux/bitops.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/interrupt.h> #include <linux/irqdomain.h> #include <linux/irqchip.h> -#include <linux/irqchip/chained_irq.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/mfd/syscon.h> @@ -39,6 +39,7 @@ struct keystone_irq_device { struct irq_domain *irqd; struct regmap *devctrl_regs; u32 devctrl_offset; + raw_spinlock_t wa_lock; }; static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq) @@ -83,17 +84,15 @@ static void keystone_irq_ack(struct irq_data *d) /* nothing to do here */ } -static void keystone_irq_handler(struct irq_desc *desc) +static irqreturn_t keystone_irq_handler(int irq, void *keystone_irq) { - unsigned int irq = irq_desc_get_irq(desc); - struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc); + struct keystone_irq_device *kirq = keystone_irq; + unsigned long wa_lock_flags; unsigned long pending; int src, virq; dev_dbg(kirq->dev, "start irq %d\n", irq); - chained_irq_enter(irq_desc_get_chip(desc), desc); - pending = keystone_irq_readl(kirq); keystone_irq_writel(kirq, pending); @@ -111,13 +110,15 @@ static void keystone_irq_handler(struct irq_desc *desc) if (!virq) dev_warn(kirq->dev, "sporious irq detected hwirq %d, virq %d\n", src, virq); + raw_spin_lock_irqsave(&kirq->wa_lock, wa_lock_flags); generic_handle_irq(virq); + raw_spin_unlock_irqrestore(&kirq->wa_lock, + wa_lock_flags); } } - chained_irq_exit(irq_desc_get_chip(desc), desc); - dev_dbg(kirq->dev, "end irq %d\n", irq); + return IRQ_HANDLED; } static int keystone_irq_map(struct irq_domain *h, unsigned int virq, @@ -182,9 +183,16 @@ static int keystone_irq_probe(struct platform_device *pdev) return -ENODEV; } + raw_spin_lock_init(&kirq->wa_lock); + platform_set_drvdata(pdev, kirq); - irq_set_chained_handler_and_data(kirq->irq, keystone_irq_handler, kirq); + ret = request_irq(kirq->irq, keystone_irq_handler, + 0, dev_name(dev), kirq); + if (ret) { + irq_domain_remove(kirq->irqd); + return ret; + } /* clear all source bits */ keystone_irq_writel(kirq, ~0x0); @@ -199,6 +207,8 @@ static int keystone_irq_remove(struct platform_device *pdev) struct keystone_irq_device *kirq = platform_get_drvdata(pdev); int hwirq; + free_irq(kirq->irq, kirq); + for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++) irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq)); diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c index 17304705f2cf..05fa9f7af53c 100644 --- a/drivers/irqchip/irq-mxs.c +++ b/drivers/irqchip/irq-mxs.c @@ -131,12 +131,16 @@ static struct irq_chip mxs_icoll_chip = { .irq_ack = icoll_ack_irq, .irq_mask = icoll_mask_irq, .irq_unmask = icoll_unmask_irq, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SKIP_SET_WAKE, }; static struct irq_chip asm9260_icoll_chip = { .irq_ack = icoll_ack_irq, .irq_mask = asm9260_mask_irq, .irq_unmask = asm9260_unmask_irq, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SKIP_SET_WAKE, }; asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs) diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index 9b856e1890d1..e4c43a17b333 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -1379,6 +1379,7 @@ isdn_ioctl(struct file *file, uint cmd, ulong arg) if (arg) { if (copy_from_user(bname, argp, sizeof(bname) - 1)) return -EFAULT; + bname[sizeof(bname)-1] = 0; } else return -EINVAL; ret = mutex_lock_interruptible(&dev->mtx); diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index aa5dd5668528..dbad5c431bcb 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -2611,10 +2611,9 @@ isdn_net_newslave(char *parm) char newname[10]; if (p) { - /* Slave-Name MUST not be empty */ - if (!strlen(p + 1)) + /* Slave-Name MUST not be empty or overflow 'newname' */ + if (strscpy(newname, p + 1, sizeof(newname)) <= 0) return NULL; - strcpy(newname, p + 1); *p = 0; /* Master must already exist */ if (!(n = isdn_net_findif(parm))) diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 9c1e8adaf4fc..bf3fbd00a091 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -2364,7 +2364,7 @@ static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_s id); return NULL; } else { - rs = kzalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL); + rs = kzalloc(sizeof(struct ippp_ccp_reset_state), GFP_ATOMIC); if (!rs) return NULL; rs->state = CCPResetIdle; diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 564d20079715..15c931bbbf65 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -158,6 +158,11 @@ #define FLASH_LED_DISABLE 0x00 #define FLASH_LED_SAFETY_TMR_DISABLED 0x13 #define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750 +#define FLASH_LED_IRES5P0_MAX_CURR_MA 640 +#define FLASH_LED_IRES7P5_MAX_CURR_MA 960 +#define FLASH_LED_IRES10P0_MAX_CURR_MA 1280 +#define FLASH_LED_IRES12P5_MAX_CURR_MA 1600 +#define MAX_IRES_LEVELS 4 /* notifier call chain for flash-led irqs */ static ATOMIC_NOTIFIER_HEAD(irq_notifier_list); @@ -196,13 +201,15 @@ struct flash_node_data { struct pinctrl_state *hw_strobe_state_suspend; int hw_strobe_gpio; int ires_ua; + int default_ires_ua; int max_current; int current_ma; int prev_current_ma; u8 duration; u8 id; u8 type; - u8 ires; + u8 ires_idx; + u8 default_ires_idx; u8 hdrm_val; u8 current_reg_val; u8 strobe_ctrl; @@ -305,6 +312,11 @@ static int otst3_threshold_table[] = { 125, 119, 113, 107, 149, 143, 137, 131, }; +static int max_ires_curr_ma_table[MAX_IRES_LEVELS] = { + FLASH_LED_IRES12P5_MAX_CURR_MA, FLASH_LED_IRES10P0_MAX_CURR_MA, + FLASH_LED_IRES7P5_MAX_CURR_MA, FLASH_LED_IRES5P0_MAX_CURR_MA +}; + static int qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data) { int rc; @@ -935,6 +947,7 @@ static void qpnp_flash_led_aggregate_max_current(struct flash_node_data *fnode) static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) { + int i = 0; int prgm_current_ma = value; int min_ma = fnode->ires_ua / 1000; struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev); @@ -944,7 +957,22 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) else if (value < min_ma) prgm_current_ma = min_ma; + fnode->ires_idx = fnode->default_ires_idx; + fnode->ires_ua = fnode->default_ires_ua; + prgm_current_ma = min(prgm_current_ma, fnode->max_current); + if (prgm_current_ma > max_ires_curr_ma_table[fnode->ires_idx]) { + /* find the matching ires */ + for (i = MAX_IRES_LEVELS - 1; i >= 0; i--) { + if (prgm_current_ma <= max_ires_curr_ma_table[i]) { + fnode->ires_idx = i; + fnode->ires_ua = FLASH_LED_IRES_MIN_UA + + (FLASH_LED_IRES_BASE - fnode->ires_idx) * + FLASH_LED_IRES_DIVISOR; + break; + } + } + } fnode->current_ma = prgm_current_ma; fnode->cdev.brightness = prgm_current_ma; fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma, @@ -1062,7 +1090,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) val = 0; for (i = 0; i < led->num_fnodes; i++) if (snode->led_mask & BIT(led->fnode[i].id)) - val |= led->fnode[i].ires << (led->fnode[i].id * 2); + val |= led->fnode[i].ires_idx << (led->fnode[i].id * 2); rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_IRES(led->base), FLASH_LED_CURRENT_MASK, val); @@ -1434,13 +1462,14 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, return rc; } - fnode->ires_ua = FLASH_LED_IRES_DEFAULT_UA; - fnode->ires = FLASH_LED_IRES_DEFAULT_VAL; + fnode->default_ires_ua = fnode->ires_ua = FLASH_LED_IRES_DEFAULT_UA; + fnode->default_ires_idx = fnode->ires_idx = FLASH_LED_IRES_DEFAULT_VAL; rc = of_property_read_u32(node, "qcom,ires-ua", &val); if (!rc) { - fnode->ires_ua = val; - fnode->ires = FLASH_LED_IRES_BASE - - (val - FLASH_LED_IRES_MIN_UA) / FLASH_LED_IRES_DIVISOR; + fnode->default_ires_ua = fnode->ires_ua = val; + fnode->default_ires_idx = fnode->ires_idx = + FLASH_LED_IRES_BASE - (val - FLASH_LED_IRES_MIN_UA) / + FLASH_LED_IRES_DIVISOR; } else if (rc != -EINVAL) { pr_err("Unable to read current resolution rc=%d\n", rc); return rc; diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c index 950244f1e4e8..c85b3e42c8c8 100644 --- a/drivers/leds/leds-qpnp-wled.c +++ b/drivers/leds/leds-qpnp-wled.c @@ -160,18 +160,19 @@ #define QPNP_WLED_MOD_EN_SHFT 7 #define QPNP_WLED_MOD_EN 1 #define QPNP_WLED_GATE_DRV_MASK 0xFE -#define QPNP_WLED_SYNC_DLY_MASK 0xF8 +#define QPNP_WLED_SYNC_DLY_MASK GENMASK(2, 0) #define QPNP_WLED_SYNC_DLY_MIN_US 0 #define QPNP_WLED_SYNC_DLY_MAX_US 1400 #define QPNP_WLED_SYNC_DLY_STEP_US 200 #define QPNP_WLED_DEF_SYNC_DLY_US 400 -#define QPNP_WLED_FS_CURR_MASK 0xF0 +#define QPNP_WLED_FS_CURR_MASK GENMASK(3, 0) #define QPNP_WLED_FS_CURR_MIN_UA 0 #define QPNP_WLED_FS_CURR_MAX_UA 30000 #define QPNP_WLED_FS_CURR_STEP_UA 2500 -#define QPNP_WLED_CABC_MASK 0x7F +#define QPNP_WLED_CABC_MASK 0x80 #define QPNP_WLED_CABC_SHIFT 7 #define QPNP_WLED_CURR_SINK_SHIFT 4 +#define QPNP_WLED_CURR_SINK_MASK GENMASK(7, 4) #define QPNP_WLED_BRIGHT_LSB_MASK 0xFF #define QPNP_WLED_BRIGHT_MSB_SHIFT 8 #define QPNP_WLED_BRIGHT_MSB_MASK 0x0F @@ -208,12 +209,14 @@ #define QPNP_WLED_SEC_UNLOCK 0xA5 #define QPNP_WLED_MAX_STRINGS 4 +#define QPNP_PM660_WLED_MAX_STRINGS 3 #define WLED_MAX_LEVEL_4095 4095 #define QPNP_WLED_RAMP_DLY_MS 20 #define QPNP_WLED_TRIGGER_NONE "none" #define QPNP_WLED_STR_SIZE 20 #define QPNP_WLED_MIN_MSLEEP 20 #define QPNP_WLED_SC_DLY_MS 20 +#define QPNP_WLED_SOFT_START_DLY_US 10000 #define NUM_SUPPORTED_AVDD_VOLTAGES 6 #define QPNP_WLED_DFLT_AVDD_MV 7600 @@ -381,6 +384,8 @@ struct qpnp_wled { u16 ramp_ms; u16 ramp_step; u16 cons_sync_write_delay_us; + u16 auto_calibration_ovp_count; + u16 max_strings; u8 strings[QPNP_WLED_MAX_STRINGS]; u8 num_strings; u8 loop_auto_gm_thresh; @@ -396,6 +401,9 @@ struct qpnp_wled { bool en_ext_pfet_sc_pro; bool prev_state; bool ovp_irq_disabled; + bool auto_calib_enabled; + bool auto_calib_done; + ktime_t start_ovp_fault_time; }; /* helper to read a pmic register */ @@ -529,9 +537,14 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) { int i, rc; u8 reg; + u16 low_limit = WLED_MAX_LEVEL_4095 * 4 / 1000; + + /* WLED's lower limit of operation is 0.4% */ + if (level > 0 && level < low_limit) + level = low_limit; /* set brightness registers */ - for (i = 0; i < wled->num_strings; i++) { + for (i = 0; i < wled->max_strings; i++) { reg = level & QPNP_WLED_BRIGHT_LSB_MASK; rc = qpnp_wled_write_reg(wled, QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base, @@ -600,7 +613,8 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled, * OVP interrupt disabled when the module is disabled. */ if (state) { - usleep_range(10000, 11000); + usleep_range(QPNP_WLED_SOFT_START_DLY_US, + QPNP_WLED_SOFT_START_DLY_US + 1000); rc = qpnp_wled_psm_config(wled, false); if (rc < 0) return rc; @@ -873,32 +887,25 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qpnp_wled *wled = dev_get_drvdata(dev); - int data, i, rc, temp; + int data, i, rc; u8 reg; rc = kstrtoint(buf, 10, &data); if (rc) return rc; - for (i = 0; i < wled->num_strings; i++) { + for (i = 0; i < wled->max_strings; i++) { if (data < QPNP_WLED_FS_CURR_MIN_UA) data = QPNP_WLED_FS_CURR_MIN_UA; else if (data > QPNP_WLED_FS_CURR_MAX_UA) data = QPNP_WLED_FS_CURR_MAX_UA; - rc = qpnp_wled_read_reg(wled, - QPNP_WLED_FS_CURR_REG(wled->sink_base, - wled->strings[i]), ®); + reg = data / QPNP_WLED_FS_CURR_STEP_UA; + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_FS_CURR_REG(wled->sink_base, i), + QPNP_WLED_FS_CURR_MASK, reg); if (rc < 0) return rc; - reg &= QPNP_WLED_FS_CURR_MASK; - temp = data / QPNP_WLED_FS_CURR_STEP_UA; - reg |= temp; - rc = qpnp_wled_write_reg(wled, - QPNP_WLED_FS_CURR_REG(wled->sink_base, - wled->strings[i]), reg); - if (rc) - return rc; } wled->fs_curr_ua = data; @@ -1090,6 +1097,229 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) return 0; } +#define AUTO_CALIB_BRIGHTNESS 16 +static int wled_auto_calibrate(struct qpnp_wled *wled) +{ + int rc = 0, i; + u8 reg = 0, sink_config = 0, sink_test = 0, sink_valid = 0, int_sts; + + mutex_lock(&wled->lock); + + /* disable OVP IRQ */ + if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) { + disable_irq_nosync(wled->ovp_irq); + wled->ovp_irq_disabled = true; + } + + /* read configured sink configuration */ + rc = qpnp_wled_read_reg(wled, + QPNP_WLED_CURR_SINK_REG(wled->sink_base), &sink_config); + if (rc < 0) { + pr_err("Failed to read SINK configuration rc=%d\n", rc); + goto failed_calib; + } + + /* disable the module before starting calibration */ + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), + QPNP_WLED_MODULE_EN_MASK, 0); + if (rc < 0) { + pr_err("Failed to disable WLED module rc=%d\n", rc); + goto failed_calib; + } + + /* set low brightness across all sinks */ + rc = qpnp_wled_set_level(wled, AUTO_CALIB_BRIGHTNESS); + if (rc < 0) { + pr_err("Failed to set brightness for calibration rc=%d\n", rc); + goto failed_calib; + } + + /* disable all sinks */ + rc = qpnp_wled_write_reg(wled, + QPNP_WLED_CURR_SINK_REG(wled->sink_base), 0); + if (rc < 0) { + pr_err("Failed to disable all sinks rc=%d\n", rc); + goto failed_calib; + } + + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), + QPNP_WLED_MODULE_EN_MASK, + QPNP_WLED_MODULE_EN_MASK); + if (rc < 0) { + pr_err("Failed to enable WLED module rc=%d\n", rc); + goto failed_calib; + } + /* + * Delay for the WLED soft-start, check the OVP status + * only after soft-start is complete + */ + usleep_range(QPNP_WLED_SOFT_START_DLY_US, + QPNP_WLED_SOFT_START_DLY_US + 1000); + + /* iterate through the strings one by one */ + for (i = 0; i < wled->max_strings; i++) { + sink_test = 1 << (QPNP_WLED_CURR_SINK_SHIFT + i); + + /* Enable feedback control */ + rc = qpnp_wled_write_reg(wled, + QPNP_WLED_FDBK_OP_REG(wled->ctrl_base), + i + 1); + if (rc < 0) { + pr_err("Failed to enable feedback for SINK %d rc = %d\n", + i + 1, rc); + goto failed_calib; + } + + /* enable the sink */ + rc = qpnp_wled_write_reg(wled, + QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_test); + if (rc < 0) { + pr_err("Failed to configure SINK %d rc=%d\n", + i + 1, rc); + goto failed_calib; + } + + /* delay for WLED soft-start */ + usleep_range(QPNP_WLED_SOFT_START_DLY_US, + QPNP_WLED_SOFT_START_DLY_US + 1000); + + rc = qpnp_wled_read_reg(wled, + QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts); + if (rc < 0) { + pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc); + goto failed_calib; + } + + if (int_sts & QPNP_WLED_OVP_FAULT_BIT) + pr_debug("WLED OVP fault detected with SINK %d\n", + i + 1); + else + sink_valid |= sink_test; + } + + if (sink_valid == sink_config) { + pr_debug("WLED auto-calibration complete, default sink-config=%x OK!\n", + sink_config); + } else { + pr_warn("Invalid WLED default sink config=%x changing it to=%x\n", + sink_config, sink_valid); + sink_config = sink_valid; + } + + if (!sink_config) { + pr_warn("No valid WLED sinks found\n"); + goto failed_calib; + } + + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), + QPNP_WLED_MODULE_EN_MASK, 0); + if (rc < 0) { + pr_err("Failed to disable WLED module rc=%d\n", rc); + goto failed_calib; + } + + /* write the new sink configuration */ + rc = qpnp_wled_write_reg(wled, + QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_config); + if (rc < 0) { + pr_err("Failed to reconfigure the default sink rc=%d\n", rc); + goto failed_calib; + } + + /* MODULATOR_EN setting for valid sinks */ + for (i = 0; i < wled->max_strings; i++) { + if (sink_config & (1 << (QPNP_WLED_CURR_SINK_SHIFT + i))) + reg = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT); + else + reg = 0x0; /* disable modulator_en for unused sink */ + + if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) + reg &= QPNP_WLED_GATE_DRV_MASK; + else + reg |= ~QPNP_WLED_GATE_DRV_MASK; + + rc = qpnp_wled_write_reg(wled, + QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg); + if (rc < 0) { + pr_err("Failed to configure MODULATOR_EN rc=%d\n", rc); + goto failed_calib; + } + } + + /* restore the feedback setting */ + rc = qpnp_wled_write_reg(wled, + QPNP_WLED_FDBK_OP_REG(wled->ctrl_base), + wled->fdbk_op); + if (rc < 0) { + pr_err("Failed to restore feedback setting rc=%d\n", rc); + goto failed_calib; + } + + /* restore brightness */ + rc = qpnp_wled_set_level(wled, wled->cdev.brightness); + if (rc < 0) { + pr_err("Failed to set brightness after calibration rc=%d\n", + rc); + goto failed_calib; + } + + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), + QPNP_WLED_MODULE_EN_MASK, + QPNP_WLED_MODULE_EN_MASK); + if (rc < 0) { + pr_err("Failed to enable WLED module rc=%d\n", rc); + goto failed_calib; + } + + /* delay for WLED soft-start */ + usleep_range(QPNP_WLED_SOFT_START_DLY_US, + QPNP_WLED_SOFT_START_DLY_US + 1000); + +failed_calib: + if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) { + enable_irq(wled->ovp_irq); + wled->ovp_irq_disabled = false; + } + mutex_unlock(&wled->lock); + return rc; +} + +#define WLED_AUTO_CAL_OVP_COUNT 5 +#define WLED_AUTO_CAL_CNT_DLY_US 1000000 /* 1 second */ +static bool qpnp_wled_auto_cal_required(struct qpnp_wled *wled) +{ + s64 elapsed_time_us; + + /* + * Check if the OVP fault was an occasional one + * or if its firing continuously, the latter qualifies + * for an auto-calibration check. + */ + if (!wled->auto_calibration_ovp_count) { + wled->start_ovp_fault_time = ktime_get(); + wled->auto_calibration_ovp_count++; + } else { + elapsed_time_us = ktime_us_delta(ktime_get(), + wled->start_ovp_fault_time); + if (elapsed_time_us > WLED_AUTO_CAL_CNT_DLY_US) + wled->auto_calibration_ovp_count = 0; + else + wled->auto_calibration_ovp_count++; + + if (wled->auto_calibration_ovp_count >= + WLED_AUTO_CAL_OVP_COUNT) { + wled->auto_calibration_ovp_count = 0; + return true; + } + } + + return false; +} + /* ovp irq handler */ static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled) { @@ -1114,6 +1344,21 @@ static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled) if (fault_sts & (QPNP_WLED_OVP_FAULT_BIT | QPNP_WLED_ILIM_FAULT_BIT)) pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n", int_sts, fault_sts); + + if (fault_sts & QPNP_WLED_OVP_FAULT_BIT) { + if (wled->auto_calib_enabled && !wled->auto_calib_done) { + if (qpnp_wled_auto_cal_required(wled)) { + rc = wled_auto_calibrate(wled); + if (rc < 0) { + pr_err("Failed auto-calibration rc=%d\n", + rc); + return IRQ_HANDLED; + } + wled->auto_calib_done = true; + } + } + } + return IRQ_HANDLED; } @@ -1423,7 +1668,7 @@ static int qpnp_wled_vref_config(struct qpnp_wled *wled) static int qpnp_wled_config(struct qpnp_wled *wled) { int rc, i, temp; - u8 reg = 0; + u8 reg = 0, sink_en = 0, mask; /* Configure display type */ rc = qpnp_wled_set_disp(wled, wled->ctrl_base); @@ -1622,93 +1867,77 @@ static int qpnp_wled_config(struct qpnp_wled *wled) rc = qpnp_wled_write_reg(wled, QPNP_WLED_CURR_SINK_REG(wled->sink_base), reg); - for (i = 0; i < wled->num_strings; i++) { - if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) { - dev_err(&wled->pdev->dev, "Invalid string number\n"); - return -EINVAL; - } - - /* MODULATOR */ - rc = qpnp_wled_read_reg(wled, - QPNP_WLED_MOD_EN_REG(wled->sink_base, - wled->strings[i]), ®); - if (rc < 0) - return rc; - reg &= QPNP_WLED_MOD_EN_MASK; - reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT); - - if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) - reg &= QPNP_WLED_GATE_DRV_MASK; - else - reg |= ~QPNP_WLED_GATE_DRV_MASK; - - rc = qpnp_wled_write_reg(wled, - QPNP_WLED_MOD_EN_REG(wled->sink_base, - wled->strings[i]), reg); - if (rc) - return rc; - + for (i = 0; i < wled->max_strings; i++) { /* SYNC DELAY */ if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US) wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US; - rc = qpnp_wled_read_reg(wled, - QPNP_WLED_SYNC_DLY_REG(wled->sink_base, - wled->strings[i]), ®); + reg = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US; + mask = QPNP_WLED_SYNC_DLY_MASK; + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_SYNC_DLY_REG(wled->sink_base, i), + mask, reg); if (rc < 0) return rc; - reg &= QPNP_WLED_SYNC_DLY_MASK; - temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US; - reg |= temp; - rc = qpnp_wled_write_reg(wled, - QPNP_WLED_SYNC_DLY_REG(wled->sink_base, - wled->strings[i]), reg); - if (rc) - return rc; /* FULL SCALE CURRENT */ if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA) wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA; - rc = qpnp_wled_read_reg(wled, - QPNP_WLED_FS_CURR_REG(wled->sink_base, - wled->strings[i]), ®); + reg = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA; + mask = QPNP_WLED_FS_CURR_MASK; + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_FS_CURR_REG(wled->sink_base, i), + mask, reg); if (rc < 0) return rc; - reg &= QPNP_WLED_FS_CURR_MASK; - temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA; - reg |= temp; - rc = qpnp_wled_write_reg(wled, - QPNP_WLED_FS_CURR_REG(wled->sink_base, - wled->strings[i]), reg); - if (rc) - return rc; /* CABC */ - rc = qpnp_wled_read_reg(wled, - QPNP_WLED_CABC_REG(wled->sink_base, - wled->strings[i]), ®); + reg = wled->en_cabc ? (1 << QPNP_WLED_CABC_SHIFT) : 0; + mask = QPNP_WLED_CABC_MASK; + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_CABC_REG(wled->sink_base, i), + mask, reg); if (rc < 0) return rc; - reg &= QPNP_WLED_CABC_MASK; - reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT); - rc = qpnp_wled_write_reg(wled, - QPNP_WLED_CABC_REG(wled->sink_base, - wled->strings[i]), reg); - if (rc) - return rc; + } - /* Enable CURRENT SINK */ + /* Settings specific to valid sinks */ + for (i = 0; i < wled->num_strings; i++) { + if (wled->strings[i] >= wled->max_strings) { + dev_err(&wled->pdev->dev, "Invalid string number\n"); + return -EINVAL; + } + /* MODULATOR */ rc = qpnp_wled_read_reg(wled, - QPNP_WLED_CURR_SINK_REG(wled->sink_base), ®); + QPNP_WLED_MOD_EN_REG(wled->sink_base, i), ®); if (rc < 0) return rc; - temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT; - reg |= (1 << temp); + reg &= QPNP_WLED_MOD_EN_MASK; + reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT); + + if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) + reg &= QPNP_WLED_GATE_DRV_MASK; + else + reg |= ~QPNP_WLED_GATE_DRV_MASK; + rc = qpnp_wled_write_reg(wled, - QPNP_WLED_CURR_SINK_REG(wled->sink_base), reg); + QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg); if (rc) return rc; + + /* SINK EN */ + temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT; + sink_en |= (1 << temp); + } + mask = QPNP_WLED_CURR_SINK_MASK; + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_CURR_SINK_REG(wled->sink_base), + mask, sink_en); + if (rc < 0) { + dev_err(&wled->pdev->dev, + "Failed to enable WLED sink config rc = %d\n", rc); + return rc; } rc = qpnp_wled_sync_reg_toggle(wled); @@ -1728,8 +1957,13 @@ static int qpnp_wled_config(struct qpnp_wled *wled) wled->ovp_irq, rc); return rc; } - disable_irq(wled->ovp_irq); - wled->ovp_irq_disabled = true; + rc = qpnp_wled_read_reg(wled, + QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), ®); + /* disable the OVP irq only if the module is not enabled */ + if (!rc && !(reg & QPNP_WLED_MODULE_EN_MASK)) { + disable_irq(wled->ovp_irq); + wled->ovp_irq_disabled = true; + } } if (wled->sc_irq >= 0) { @@ -2091,11 +2325,16 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) wled->en_cabc = of_property_read_bool(pdev->dev.of_node, "qcom,en-cabc"); + if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) + wled->max_strings = QPNP_PM660_WLED_MAX_STRINGS; + else + wled->max_strings = QPNP_WLED_MAX_STRINGS; + prop = of_find_property(pdev->dev.of_node, "qcom,led-strings-list", &temp_val); if (!prop || !temp_val || temp_val > QPNP_WLED_MAX_STRINGS) { dev_err(&pdev->dev, "Invalid strings info, use default"); - wled->num_strings = QPNP_WLED_MAX_STRINGS; + wled->num_strings = wled->max_strings; for (i = 0; i < wled->num_strings; i++) wled->strings[i] = i; } else { @@ -2118,6 +2357,9 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) wled->lcd_psm_ctrl = of_property_read_bool(pdev->dev.of_node, "qcom,lcd-psm-ctrl"); + + wled->auto_calib_enabled = of_property_read_bool(pdev->dev.of_node, + "qcom,auto-calibration-enable"); return 0; } @@ -2185,13 +2427,13 @@ static int qpnp_wled_probe(struct platform_device *pdev) } mutex_init(&wled->bus_lock); + mutex_init(&wled->lock); rc = qpnp_wled_config(wled); if (rc) { dev_err(&pdev->dev, "wled config failed\n"); return rc; } - mutex_init(&wled->lock); INIT_WORK(&wled->work, qpnp_wled_work); wled->ramp_ms = QPNP_WLED_RAMP_DLY_MS; wled->ramp_step = 1; diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c index ab0e4f99ebb9..e3cfffb5c563 100644 --- a/drivers/leds/leds-qpnp.c +++ b/drivers/leds/leds-qpnp.c @@ -875,6 +875,14 @@ static int qpnp_mpp_set(struct qpnp_led_data *led) } if (led->mpp_cfg->pwm_mode == PWM_MODE) { /*config pwm for brightness scaling*/ + rc = pwm_change_mode(led->mpp_cfg->pwm_cfg->pwm_dev, + PM_PWM_MODE_PWM); + if (rc < 0) { + dev_err(&led->pdev->dev, + "Failed to set PWM mode, rc = %d\n", + rc); + return rc; + } period_us = led->mpp_cfg->pwm_cfg->pwm_period_us; if (period_us > INT_MAX / NSEC_PER_USEC) { duty_us = (period_us * led->cdev.brightness) / @@ -1581,6 +1589,14 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) } if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) { + rc = pwm_change_mode(led->kpdbl_cfg->pwm_cfg->pwm_dev, + PM_PWM_MODE_PWM); + if (rc < 0) { + dev_err(&led->pdev->dev, + "Failed to set PWM mode, rc = %d\n", + rc); + return rc; + } period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us; if (period_us > INT_MAX / NSEC_PER_USEC) { duty_us = (period_us * led->cdev.brightness) / @@ -1702,6 +1718,14 @@ static int qpnp_rgb_set(struct qpnp_led_data *led) led->rgb_cfg->pwm_cfg->mode = led->rgb_cfg->pwm_cfg->default_mode; if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) { + rc = pwm_change_mode(led->rgb_cfg->pwm_cfg->pwm_dev, + PM_PWM_MODE_PWM); + if (rc < 0) { + dev_err(&led->pdev->dev, + "Failed to set PWM mode, rc = %d\n", + rc); + return rc; + } period_us = led->rgb_cfg->pwm_cfg->pwm_period_us; if (period_us > INT_MAX / NSEC_PER_USEC) { duty_us = (period_us * led->cdev.brightness) / @@ -2136,6 +2160,11 @@ static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg, dev_err(&pdev->dev, "Failed to configure pwm LUT\n"); return rc; } + rc = pwm_change_mode(pwm_cfg->pwm_dev, PM_PWM_MODE_LPG); + if (rc < 0) { + dev_err(&pdev->dev, "Failed to set LPG mode\n"); + return rc; + } } } else { dev_err(&pdev->dev, "Invalid PWM device\n"); diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index 6a4811f85705..9cf826df89b1 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -104,11 +104,14 @@ static void tx_tick(struct mbox_chan *chan, int r) /* Submit next message */ msg_submit(chan); + if (!mssg) + return; + /* Notify the client */ - if (mssg && chan->cl->tx_done) + if (chan->cl->tx_done) chan->cl->tx_done(chan->cl, mssg, r); - if (chan->cl->tx_block) + if (r != -ETIME && chan->cl->tx_block) complete(&chan->tx_complete); } @@ -261,7 +264,7 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg) msg_submit(chan); - if (chan->cl->tx_block && chan->active_req) { + if (chan->cl->tx_block) { unsigned long wait; int ret; @@ -272,8 +275,8 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg) ret = wait_for_completion_timeout(&chan->tx_complete, wait); if (ret == 0) { - t = -EIO; - tx_tick(chan, -EIO); + t = -ETIME; + tx_tick(chan, t); } } diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 4f6086970131..43d566fd38ae 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -645,6 +645,8 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) android_verity_target.iterate_devices = dm_linear_iterate_devices, android_verity_target.io_hints = NULL; + set_disk_ro(dm_disk(dm_table_get_md(ti->table)), 0); + err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args); if (!err) { diff --git a/drivers/md/md.c b/drivers/md/md.c index eff554a12fb4..0a856cb181e9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1866,7 +1866,7 @@ super_1_rdev_size_change(struct md_rdev *rdev, sector_t num_sectors) } sb = page_address(rdev->sb_page); sb->data_size = cpu_to_le64(num_sectors); - sb->super_offset = rdev->sb_start; + sb->super_offset = cpu_to_le64(rdev->sb_start); sb->sb_csum = calc_sb_1_csum(sb); md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size, rdev->sb_page); @@ -2273,7 +2273,7 @@ static bool does_sb_need_changing(struct mddev *mddev) /* Check if any mddev parameters have changed */ if ((mddev->dev_sectors != le64_to_cpu(sb->size)) || (mddev->reshape_position != le64_to_cpu(sb->reshape_position)) || - (mddev->layout != le64_to_cpu(sb->layout)) || + (mddev->layout != le32_to_cpu(sb->layout)) || (mddev->raid_disks != le32_to_cpu(sb->raid_disks)) || (mddev->chunk_sectors != le32_to_cpu(sb->chunksize))) return true; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index d81be5e471d0..f24a9e14021d 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1088,7 +1088,7 @@ static void make_request(struct mddev *mddev, struct bio * bio) */ DEFINE_WAIT(w); for (;;) { - flush_signals(current); + sigset_t full, old; prepare_to_wait(&conf->wait_barrier, &w, TASK_INTERRUPTIBLE); if (bio_end_sector(bio) <= mddev->suspend_lo || @@ -1097,7 +1097,10 @@ static void make_request(struct mddev *mddev, struct bio * bio) !md_cluster_ops->area_resyncing(mddev, WRITE, bio->bi_iter.bi_sector, bio_end_sector(bio)))) break; + sigfillset(&full); + sigprocmask(SIG_BLOCK, &full, &old); schedule(); + sigprocmask(SIG_SETMASK, &old, NULL); } finish_wait(&conf->wait_barrier, &w); } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 4384b46cee1a..8f60520c8392 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -5279,12 +5279,15 @@ static void make_request(struct mddev *mddev, struct bio * bi) * userspace, we want an interruptible * wait. */ - flush_signals(current); prepare_to_wait(&conf->wait_for_overlap, &w, TASK_INTERRUPTIBLE); if (logical_sector >= mddev->suspend_lo && logical_sector < mddev->suspend_hi) { + sigset_t full, old; + sigfillset(&full); + sigprocmask(SIG_BLOCK, &full, &old); schedule(); + sigprocmask(SIG_SETMASK, &old, NULL); do_prepare = true; } goto retry; @@ -5818,6 +5821,8 @@ static void raid5_do_work(struct work_struct *work) pr_debug("%d stripes handled\n", handled); spin_unlock_irq(&conf->device_lock); + + async_tx_issue_pending_all(); blk_finish_plug(&plug); pr_debug("--- raid5worker inactive\n"); @@ -7528,12 +7533,10 @@ static void end_reshape(struct r5conf *conf) { if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) { - struct md_rdev *rdev; spin_lock_irq(&conf->device_lock); conf->previous_raid_disks = conf->raid_disks; - rdev_for_each(rdev, conf->mddev) - rdev->data_offset = rdev->new_data_offset; + md_finish_reshape(conf->mddev); smp_wmb(); conf->reshape_progress = MaxSector; conf->mddev->reshape_position = MaxSector; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c index 8001cde1db1e..503135a4f47a 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c @@ -211,7 +211,7 @@ static int s5c73m3_3a_lock(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) } if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) - ret = s5c73m3_af_run(state, ~af_lock); + ret = s5c73m3_af_run(state, !af_lock); return ret; } diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 8f2556ec3971..61611d1682d1 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -3691,7 +3691,14 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) core->nr = nr; sprintf(core->name, "cx88[%d]", core->nr); - core->tvnorm = V4L2_STD_NTSC_M; + /* + * Note: Setting initial standard here would cause first call to + * cx88_set_tvnorm() to return without programming any registers. Leave + * it blank for at this point and it will get set later in + * cx8800_initdev() + */ + core->tvnorm = 0; + core->width = 320; core->height = 240; core->field = V4L2_FIELD_INTERLACED; diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index aef9acf351f6..abbf5b05b6f5 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1429,7 +1429,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, /* initial device configuration */ mutex_lock(&core->lock); - cx88_set_tvnorm(core, core->tvnorm); + cx88_set_tvnorm(core, V4L2_STD_NTSC_M); v4l2_ctrl_handler_setup(&core->video_hdl); v4l2_ctrl_handler_setup(&core->audio_hdl); cx88_video_mux(core, 0); diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c index 8ef6399d794f..bc957528f69f 100644 --- a/drivers/media/pci/saa7134/saa7134-i2c.c +++ b/drivers/media/pci/saa7134/saa7134-i2c.c @@ -355,12 +355,43 @@ static struct i2c_client saa7134_client_template = { /* ----------------------------------------------------------- */ +/* On Medion 7134 reading EEPROM needs DVB-T demod i2c gate open */ +static void saa7134_i2c_eeprom_md7134_gate(struct saa7134_dev *dev) +{ + u8 subaddr = 0x7, dmdregval; + u8 data[2]; + int ret; + struct i2c_msg i2cgatemsg_r[] = { {.addr = 0x08, .flags = 0, + .buf = &subaddr, .len = 1}, + {.addr = 0x08, + .flags = I2C_M_RD, + .buf = &dmdregval, .len = 1} + }; + struct i2c_msg i2cgatemsg_w[] = { {.addr = 0x08, .flags = 0, + .buf = data, .len = 2} }; + + ret = i2c_transfer(&dev->i2c_adap, i2cgatemsg_r, 2); + if ((ret == 2) && (dmdregval & 0x2)) { + pr_debug("%s: DVB-T demod i2c gate was left closed\n", + dev->name); + + data[0] = subaddr; + data[1] = (dmdregval & ~0x2); + if (i2c_transfer(&dev->i2c_adap, i2cgatemsg_w, 1) != 1) + pr_err("%s: EEPROM i2c gate open failure\n", + dev->name); + } +} + static int saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len) { unsigned char buf; int i,err; + if (dev->board == SAA7134_BOARD_MD7134) + saa7134_i2c_eeprom_md7134_gate(dev); + dev->i2c_client.addr = 0xa0 >> 1; buf = 0; if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) { diff --git a/drivers/media/platform/msm/ais/fd/msm_fd_dev.c b/drivers/media/platform/msm/ais/fd/msm_fd_dev.c index 420083f019cf..d9e109938e7e 100644 --- a/drivers/media/platform/msm/ais/fd/msm_fd_dev.c +++ b/drivers/media/platform/msm/ais/fd/msm_fd_dev.c @@ -430,6 +430,7 @@ static int msm_fd_open(struct file *file) ctx->vb2_q.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ctx->vb2_q.io_modes = VB2_USERPTR; ctx->vb2_q.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + mutex_init(&ctx->lock); ret = vb2_queue_init(&ctx->vb2_q); if (ret < 0) { dev_err(device->dev, "Error queue init\n"); @@ -480,7 +481,9 @@ static int msm_fd_release(struct file *file) msm_cpp_vbif_register_error_handler((void *)ctx, VBIF_CLIENT_FD, NULL); + mutex_lock(&ctx->lock); vb2_queue_release(&ctx->vb2_q); + mutex_unlock(&ctx->lock); vfree(ctx->stats); @@ -510,7 +513,9 @@ static unsigned int msm_fd_poll(struct file *file, struct fd_ctx *ctx = msm_fd_ctx_from_fh(file->private_data); unsigned int ret; + mutex_lock(&ctx->lock); ret = vb2_poll(&ctx->vb2_q, file, wait); + mutex_unlock(&ctx->lock); if (atomic_read(&ctx->subscribed_for_event)) { poll_wait(file, &ctx->fh.wait, wait); @@ -748,9 +753,9 @@ static int msm_fd_reqbufs(struct file *file, int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - mutex_lock(&ctx->fd_device->recovery_lock); + mutex_lock(&ctx->lock); ret = vb2_reqbufs(&ctx->vb2_q, req); - mutex_unlock(&ctx->fd_device->recovery_lock); + mutex_unlock(&ctx->lock); return ret; } @@ -766,9 +771,9 @@ static int msm_fd_qbuf(struct file *file, void *fh, int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - mutex_lock(&ctx->fd_device->recovery_lock); + mutex_lock(&ctx->lock); ret = vb2_qbuf(&ctx->vb2_q, pb); - mutex_unlock(&ctx->fd_device->recovery_lock); + mutex_unlock(&ctx->lock); return ret; } @@ -785,9 +790,9 @@ static int msm_fd_dqbuf(struct file *file, int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - mutex_lock(&ctx->fd_device->recovery_lock); + mutex_lock(&ctx->lock); ret = vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK); - mutex_unlock(&ctx->fd_device->recovery_lock); + mutex_unlock(&ctx->lock); return ret; } @@ -803,7 +808,9 @@ static int msm_fd_streamon(struct file *file, struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); int ret; + mutex_lock(&ctx->lock); ret = vb2_streamon(&ctx->vb2_q, buf_type); + mutex_unlock(&ctx->lock); if (ret < 0) dev_err(ctx->fd_device->dev, "Stream on fails\n"); @@ -822,7 +829,9 @@ static int msm_fd_streamoff(struct file *file, struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); int ret; + mutex_lock(&ctx->lock); ret = vb2_streamoff(&ctx->vb2_q, buf_type); + mutex_unlock(&ctx->lock); if (ret < 0) dev_err(ctx->fd_device->dev, "Stream off fails\n"); @@ -1053,14 +1062,18 @@ static int msm_fd_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) a->value = ctx->format.size->work_size; break; case V4L2_CID_FD_WORK_MEMORY_FD: + mutex_lock(&ctx->fd_device->recovery_lock); if (ctx->work_buf.fd != -1) msm_fd_hw_unmap_buffer(&ctx->work_buf); if (a->value >= 0) { ret = msm_fd_hw_map_buffer(&ctx->mem_pool, a->value, &ctx->work_buf); - if (ret < 0) + if (ret < 0) { + mutex_unlock(&ctx->fd_device->recovery_lock); return ret; + } } + mutex_unlock(&ctx->fd_device->recovery_lock); break; default: return -EINVAL; diff --git a/drivers/media/platform/msm/ais/fd/msm_fd_dev.h b/drivers/media/platform/msm/ais/fd/msm_fd_dev.h index c15032256f4d..a7615a65d2fc 100644 --- a/drivers/media/platform/msm/ais/fd/msm_fd_dev.h +++ b/drivers/media/platform/msm/ais/fd/msm_fd_dev.h @@ -161,6 +161,7 @@ struct fd_ctx { struct msm_fd_mem_pool mem_pool; struct msm_fd_stats *stats; struct msm_fd_buf_handle work_buf; + struct mutex lock; }; /* diff --git a/drivers/media/platform/msm/ais/isp/msm_isp.h b/drivers/media/platform/msm/ais/isp/msm_isp.h index 72a76d178aa8..86974eeb4a32 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp.h +++ b/drivers/media/platform/msm/ais/isp/msm_isp.h @@ -355,6 +355,7 @@ struct msm_vfe_hardware_info { uint32_t dmi_reg_offset; uint32_t min_ab; uint32_t min_ib; + uint32_t regulator_num; const char *regulator_names[]; }; diff --git a/drivers/media/platform/msm/ais/isp/msm_isp47.c b/drivers/media/platform/msm/ais/isp/msm_isp47.c index d63282f80aca..d33dc758aef9 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp47.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp47.c @@ -2537,8 +2537,7 @@ int msm_vfe47_get_regulators(struct vfe_device *vfe_dev) int rc = 0; int i; - vfe_dev->vfe_num_regulators = - sizeof(*vfe_dev->hw_info->regulator_names) / sizeof(char *); + vfe_dev->vfe_num_regulators = vfe_dev->hw_info->regulator_num; vfe_dev->regulator_info = kzalloc(sizeof(struct msm_cam_regulator) * vfe_dev->vfe_num_regulators, GFP_KERNEL); @@ -2811,6 +2810,7 @@ struct msm_vfe_hardware_info vfe47_hw_info = { .dmi_reg_offset = 0xC2C, .axi_hw_info = &msm_vfe47_axi_hw_info, .stats_hw_info = &msm_vfe47_stats_hw_info, + .regulator_num = 3, .regulator_names = {"vdd", "camss-vdd", "mmagic-vdd"}, }; EXPORT_SYMBOL(vfe47_hw_info); diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c index 6e89544161ee..0d08cffda25c 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c @@ -891,6 +891,12 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg) struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL; struct msm_isp_sw_framskip *sw_skip_info = NULL; + if (update_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s: Invalid num_streams %d\n", + __func__, update_cmd->num_streams); + return -EINVAL; + } + /* validate request */ for (i = 0; i < update_cmd->num_streams; i++) { update_info = (struct msm_vfe_axi_stream_cfg_update_info *) diff --git a/drivers/media/platform/msm/ais/ispif/msm_ispif.c b/drivers/media/platform/msm/ais/ispif/msm_ispif.c index 8eb88364a2cb..c41f4546da5f 100644 --- a/drivers/media/platform/msm/ais/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/ais/ispif/msm_ispif.c @@ -69,6 +69,11 @@ static void msm_ispif_io_dump_reg(struct ispif_device *ispif) { if (!ispif->enb_dump_reg) return; + if (!ispif->base) { + pr_err("%s: null pointer for the ispif base\n", __func__); + return; + } + msm_camera_io_dump(ispif->base, 0x250, 0); } diff --git a/drivers/media/platform/msm/ais/msm.c b/drivers/media/platform/msm/ais/msm.c index 3c2e28eaa4f9..a3a742182e76 100644 --- a/drivers/media/platform/msm/ais/msm.c +++ b/drivers/media/platform/msm/ais/msm.c @@ -391,6 +391,9 @@ static void msm_add_sd_in_position(struct msm_sd_subdev *msm_subdev, struct msm_sd_subdev *temp_sd; list_for_each_entry(temp_sd, sd_list, list) { + if (temp_sd == msm_subdev) { + return; + } if (msm_subdev->close_seq < temp_sd->close_seq) { list_add_tail(&msm_subdev->list, &temp_sd->list); return; @@ -725,6 +728,9 @@ static long msm_private_ioctl(struct file *file, void *fh, return 0; } + if (!event_data) + return -EINVAL; + memset(&event, 0, sizeof(struct v4l2_event)); session_id = event_data->session_id; stream_id = event_data->stream_id; diff --git a/drivers/media/platform/msm/ais/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/ais/pproc/cpp/msm_cpp.c index 811ac98beead..6c50070c91ab 100644 --- a/drivers/media/platform/msm/ais/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/ais/pproc/cpp/msm_cpp.c @@ -2882,7 +2882,7 @@ end: return rc; } -static int msm_cpp_validate_input(unsigned int cmd, void *arg, +static int msm_cpp_validate_ioctl_input(unsigned int cmd, void *arg, struct msm_camera_v4l2_ioctl_t **ioctl_ptr) { switch (cmd) { @@ -2922,6 +2922,14 @@ long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd, pr_err("sd %pK\n", sd); return -EINVAL; } + + + rc = msm_cpp_validate_ioctl_input(cmd, arg, &ioctl_ptr); + if (rc != 0) { + pr_err("input validation failed\n"); + return rc; + } + cpp_dev = v4l2_get_subdevdata(sd); if (cpp_dev == NULL) { pr_err("cpp_dev is null\n"); @@ -2933,11 +2941,6 @@ long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd, return -EINVAL; } - rc = msm_cpp_validate_input(cmd, arg, &ioctl_ptr); - if (rc != 0) { - pr_err("input validation failed\n"); - return rc; - } mutex_lock(&cpp_dev->mutex); CPP_DBG("E cmd: 0x%x\n", cmd); @@ -3437,6 +3440,7 @@ STREAM_BUFF_END: } else { pr_err("%s:%d IOMMMU attach triggered in invalid state\n", __func__, __LINE__); + rc = -EINVAL; } break; } @@ -4061,7 +4065,8 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, default: pr_err_ratelimited("%s: unsupported compat type :%x LOAD %lu\n", __func__, cmd, VIDIOC_MSM_CPP_LOAD_FIRMWARE); - break; + mutex_unlock(&cpp_dev->mutex); + return -EINVAL; } mutex_unlock(&cpp_dev->mutex); @@ -4092,7 +4097,7 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, default: pr_err_ratelimited("%s: unsupported compat type :%d\n", __func__, cmd); - break; + return -EINVAL; } if (is_copytouser_req) { diff --git a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c index 1adb380f335f..40806d5a164f 100644 --- a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c @@ -1722,6 +1722,10 @@ static long msm_actuator_subdev_do_ioctl( parg = &actuator_data; break; } + break; + case VIDIOC_MSM_ACTUATOR_CFG: + pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd); + return -EINVAL; } rc = msm_actuator_subdev_ioctl(sd, cmd, parg); diff --git a/drivers/media/platform/msm/ais/sensor/cci/Makefile b/drivers/media/platform/msm/ais/sensor/cci/Makefile index 3942508c0d66..b8b8c83bc6de 100644 --- a/drivers/media/platform/msm/ais/sensor/cci/Makefile +++ b/drivers/media/platform/msm/ais/sensor/cci/Makefile @@ -2,3 +2,4 @@ ccflags-y += -Idrivers/media/platform/msm/ais ccflags-y += -Idrivers/media/platform/msm/ais/common ccflags-y += -Idrivers/media/platform/msm/ais/sensor/io obj-$(CONFIG_MSM_AIS) += msm_cci.o +obj-$(CONFIG_MSM_AIS) += msm_early_cam.o diff --git a/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.c b/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.c new file mode 100644 index 000000000000..00ec613e7303 --- /dev/null +++ b/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.c @@ -0,0 +1,279 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> +#include "msm_sd.h" +#include "msm_early_cam.h" +#include "msm_cam_cci_hwreg.h" +#include "msm_camera_io_util.h" +#include "msm_camera_dt_util.h" +#include "cam_hw_ops.h" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +#undef EARLY_CAM_DBG +#ifdef MSM_EARLY_CAM_DEBUG +#define EARLY_CAM_DBG(fmt, args...) pr_err(fmt, ##args) +#else +#define EARLY_CAM_DBG(fmt, args...) pr_debug(fmt, ##args) +#endif + +#define MSM_EARLY_CAM_DRV_NAME "msm_early_cam" +static struct platform_driver msm_early_camera_driver; +static struct early_cam_device *new_early_cam_dev; + +int msm_early_cam_disable_clocks(void) +{ + int rc = 0; + + CDBG("%s:\n", __func__); + /* Vote OFF for clocks */ + if (new_early_cam_dev == NULL) { + rc = -EINVAL; + pr_err("%s: clock structure uninitialised %d\n", __func__, + rc); + return rc; + } + + if ((new_early_cam_dev->pdev == NULL) || + (new_early_cam_dev->early_cam_clk_info == NULL) || + (new_early_cam_dev->early_cam_clk == NULL) || + (new_early_cam_dev->num_clk == 0)) { + rc = -EINVAL; + pr_err("%s: Clock details uninitialised %d\n", __func__, + rc); + return rc; + } + + rc = msm_camera_clk_enable(&new_early_cam_dev->pdev->dev, + new_early_cam_dev->early_cam_clk_info, + new_early_cam_dev->early_cam_clk, + new_early_cam_dev->num_clk, false); + if (rc < 0) { + pr_err("%s: clk disable failed %d\n", __func__, rc); + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, + CAM_AHB_SUSPEND_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote OFF AHB_CLIENT_CSIPHY %d\n", + __func__, rc); + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID, + CAM_AHB_SUSPEND_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote OFF AHB_CLIENT_CSID %d\n", + __func__, rc); + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI, + CAM_AHB_SUSPEND_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote OFF AHB_CLIENT_CCI %d\n", + __func__, rc); + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_ISPIF, + CAM_AHB_SUSPEND_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote OFF AHB_CLIENT_ISPIF %d\n", + __func__, rc); + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_VFE0, + CAM_AHB_SUSPEND_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote OFF AHB_CLIENT_VFE0 %d\n", + __func__, rc); + return rc; + } + pr_debug("Turned OFF camera clocks\n"); + return 0; + +} +static int msm_early_cam_probe(struct platform_device *pdev) +{ + int rc = 0; + + CDBG("%s: pdev %pK device id = %d\n", __func__, pdev, pdev->id); + + /* Vote for Early camera if enabled */ + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, + CAM_AHB_SVS_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID, + CAM_AHB_SVS_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI, + CAM_AHB_SVS_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_ISPIF, + CAM_AHB_SVS_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_VFE0, + CAM_AHB_SVS_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; + } + + new_early_cam_dev = kzalloc(sizeof(struct early_cam_device), + GFP_KERNEL); + if (!new_early_cam_dev) + return -ENOMEM; + + if (pdev->dev.of_node) + of_property_read_u32((&pdev->dev)->of_node, + "cell-index", &pdev->id); + + rc = msm_camera_get_clk_info_and_rates(pdev, + &new_early_cam_dev->early_cam_clk_info, + &new_early_cam_dev->early_cam_clk, + &new_early_cam_dev->early_cam_clk_rates, + &new_early_cam_dev->num_clk_cases, + &new_early_cam_dev->num_clk); + if (rc < 0) { + pr_err("%s: msm_early_cam_get_clk_info() failed", __func__); + kfree(new_early_cam_dev); + return -EFAULT; + } + + new_early_cam_dev->ref_count = 0; + new_early_cam_dev->pdev = pdev; + + rc = msm_camera_get_dt_vreg_data( + new_early_cam_dev->pdev->dev.of_node, + &(new_early_cam_dev->early_cam_vreg), + &(new_early_cam_dev->regulator_count)); + if (rc < 0) { + pr_err("%s: msm_camera_get_dt_vreg_data fail\n", __func__); + rc = -EFAULT; + goto early_cam_release_mem; + } + + if ((new_early_cam_dev->regulator_count < 0) || + (new_early_cam_dev->regulator_count > MAX_REGULATOR)) { + pr_err("%s: invalid reg count = %d, max is %d\n", __func__, + new_early_cam_dev->regulator_count, MAX_REGULATOR); + rc = -EFAULT; + goto early_cam_invalid_vreg_data; + } + + rc = msm_camera_config_vreg(&new_early_cam_dev->pdev->dev, + new_early_cam_dev->early_cam_vreg, + new_early_cam_dev->regulator_count, + NULL, + 0, + &new_early_cam_dev->early_cam_reg_ptr[0], 1); + if (rc < 0) + pr_err("%s:%d early_cam config_vreg failed\n", __func__, + __LINE__); + + rc = msm_camera_enable_vreg(&new_early_cam_dev->pdev->dev, + new_early_cam_dev->early_cam_vreg, + new_early_cam_dev->regulator_count, + NULL, + 0, + &new_early_cam_dev->early_cam_reg_ptr[0], 1); + if (rc < 0) + pr_err("%s:%d early_cam enable_vreg failed\n", __func__, + __LINE__); + + rc = msm_camera_clk_enable(&new_early_cam_dev->pdev->dev, + new_early_cam_dev->early_cam_clk_info, + new_early_cam_dev->early_cam_clk, + new_early_cam_dev->num_clk, true); + + if (rc < 0) { + pr_err("%s: clk enable failed %d\n", __func__, rc); + rc = 0; + goto early_cam_release_mem; + } + + platform_set_drvdata(pdev, new_early_cam_dev); + + return 0; + +early_cam_invalid_vreg_data: + kfree(new_early_cam_dev->early_cam_vreg); +early_cam_release_mem: + kfree(new_early_cam_dev); + new_early_cam_dev = NULL; + return rc; +} + +static int msm_early_cam_exit(struct platform_device *pdev) +{ + return 0; +} + +static int __init msm_early_cam_init_module(void) +{ + return platform_driver_register(&msm_early_camera_driver); +} + +static void __exit msm_early_cam_exit_module(void) +{ + kfree(new_early_cam_dev); + platform_driver_unregister(&msm_early_camera_driver); +} + +static const struct of_device_id msm_early_camera_match_table[] = { + { .compatible = "qcom,early-cam" }, + {}, +}; + +static struct platform_driver msm_early_camera_driver = { + .probe = msm_early_cam_probe, + .remove = msm_early_cam_exit, + .driver = { + .name = MSM_EARLY_CAM_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_early_camera_match_table, + }, +}; + +MODULE_DEVICE_TABLE(of, msm_early_camera_match_table); + +module_init(msm_early_cam_init_module); +module_exit(msm_early_cam_exit_module); +MODULE_DESCRIPTION("MSM early camera driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.h b/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.h new file mode 100644 index 000000000000..a40ab2d1ebc3 --- /dev/null +++ b/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.h @@ -0,0 +1,53 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MSM_EARLY_CAM_H +#define MSM_EARLY_CAM_H + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <media/v4l2-subdev.h> +#include <linux/workqueue.h> +#include <media/ais/msm_ais_sensor.h> +#include <soc/qcom/ais.h> +#include "msm_sd.h" +#include "cam_soc_api.h" + +#define NUM_MASTERS 2 +#define NUM_QUEUES 2 + +#define TRUE 1 +#define FALSE 0 + + +enum msm_early_cam_state_t { + STATE_DISABLED, + STATE_ENABLED, +}; + +struct early_cam_device { + struct platform_device *pdev; + uint8_t ref_count; + enum msm_early_cam_state_t early_cam_state; + size_t num_clk; + size_t num_clk_cases; + struct clk **early_cam_clk; + uint32_t **early_cam_clk_rates; + struct msm_cam_clk_info *early_cam_clk_info; + struct camera_vreg_t *early_cam_vreg; + struct regulator *early_cam_reg_ptr[MAX_REGULATOR]; + int32_t regulator_count; +}; + +int msm_early_cam_disable_clocks(void); +#endif diff --git a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c index a2a89b92c9f1..024677e1b755 100644 --- a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c @@ -152,6 +152,13 @@ static int32_t msm_flash_i2c_write_table( conf_array.reg_setting = settings->reg_setting_a; conf_array.size = settings->size; + /* Validate the settings size */ + if ((!conf_array.size) || (conf_array.size > MAX_I2C_REG_SET)) { + pr_err("failed: invalid size %d", conf_array.size); + return -EINVAL; + } + + return flash_ctrl->flash_i2c_client.i2c_func_tbl->i2c_write_table( &flash_ctrl->flash_i2c_client, &conf_array); } @@ -497,23 +504,46 @@ static int32_t msm_flash_init( } flash_ctrl->flash_state = MSM_CAMERA_FLASH_INIT; - CDBG("Exit"); return 0; } -#ifdef CONFIG_COMPAT static int32_t msm_flash_init_prepare( struct msm_flash_ctrl_t *flash_ctrl, struct msm_flash_cfg_data_t *flash_data) { + #ifdef CONFIG_COMPAT + struct msm_flash_cfg_data_t flash_data_k; + struct msm_flash_init_info_t flash_init_info; + int32_t i = 0; + + if (!is_compat_task()) { + /*for 64-bit usecase,it need copy the data to local memory*/ + flash_data_k.cfg_type = flash_data->cfg_type; + for (i = 0; i < MAX_LED_TRIGGERS; i++) { + flash_data_k.flash_current[i] = + flash_data->flash_current[i]; + flash_data_k.flash_duration[i] = + flash_data->flash_duration[i]; + } + + flash_data_k.cfg.flash_init_info = &flash_init_info; + if (copy_from_user(&flash_init_info, + (void __user *)(flash_data->cfg.flash_init_info), + sizeof(struct msm_flash_init_info_t))) { + pr_err("%s copy_from_user failed %d\n", + __func__, __LINE__); + return -EFAULT; + } + return msm_flash_init(flash_ctrl, &flash_data_k); + } + /* + * for 32-bit usecase,it already copy the userspace + * data to local memory in msm_flash_subdev_do_ioctl() + * so here do not need copy from user + */ return msm_flash_init(flash_ctrl, flash_data); -} #else -static int32_t msm_flash_init_prepare( - struct msm_flash_ctrl_t *flash_ctrl, - struct msm_flash_cfg_data_t *flash_data) -{ struct msm_flash_cfg_data_t flash_data_k; struct msm_flash_init_info_t flash_init_info; int32_t i = 0; @@ -528,15 +558,15 @@ static int32_t msm_flash_init_prepare( flash_data_k.cfg.flash_init_info = &flash_init_info; if (copy_from_user(&flash_init_info, - (void *)(flash_data->cfg.flash_init_info), + (void __user *)(flash_data->cfg.flash_init_info), sizeof(struct msm_flash_init_info_t))) { pr_err("%s copy_from_user failed %d\n", __func__, __LINE__); return -EFAULT; } return msm_flash_init(flash_ctrl, &flash_data_k); -} #endif +} static int32_t msm_flash_low( struct msm_flash_ctrl_t *flash_ctrl, diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c index c02972e5e993..58bddb1a3fba 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c @@ -105,7 +105,11 @@ static int32_t msm_sensor_driver_create_i2c_v4l_subdev s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name; s_ctrl->sensordata->sensor_info->session_id = session_id; s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3; - msm_sd_register(&s_ctrl->msm_sd); + rc = msm_sd_register(&s_ctrl->msm_sd); + if (rc < 0) { + pr_err("failed: msm_sd_register rc %d", rc); + return rc; + } msm_sensor_v4l2_subdev_fops = v4l2_subdev_fops; #ifdef CONFIG_COMPAT msm_sensor_v4l2_subdev_fops.compat_ioctl32 = @@ -133,7 +137,11 @@ static int32_t msm_sensor_driver_create_v4l_subdev s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR; s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name; s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3; - msm_sd_register(&s_ctrl->msm_sd); + rc = msm_sd_register(&s_ctrl->msm_sd); + if (rc < 0) { + pr_err("failed: msm_sd_register rc %d", rc); + return rc; + } msm_cam_copy_v4l2_subdev_fops(&msm_sensor_v4l2_subdev_fops); #ifdef CONFIG_COMPAT msm_sensor_v4l2_subdev_fops.compat_ioctl32 = @@ -888,11 +896,6 @@ CSID_TG: pr_err("%s probe succeeded", slave_info->sensor_name); - /* Set probe succeeded flag to 1 so that no other camera shall - * probed on this slot - */ - s_ctrl->is_probe_succeed = 1; - /* * Update the subdevice id of flash-src based on availability in kernel. */ @@ -931,6 +934,10 @@ CSID_TG: msm_sensor_fill_sensor_info(s_ctrl, probed_info, entity_name); + /* Set probe succeeded flag to 1 so that no other camera shall + * probed on this slot + */ + s_ctrl->is_probe_succeed = 1; return rc; free_camera_info: diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor_init.c b/drivers/media/platform/msm/ais/sensor/msm_sensor_init.c index c3943be78226..ffbf963e819e 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor_init.c +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor_init.c @@ -16,11 +16,17 @@ #include "msm_sensor_driver.h" #include "msm_sensor.h" #include "msm_sd.h" +#include "msm_camera_io_util.h" +#include "msm_early_cam.h" /* Logging macro */ #undef CDBG #define CDBG(fmt, args...) pr_debug(fmt, ##args) +#define EARLY_CAMERA_SIGNAL_DONE 0xa5a5a5a5 +#define EARLY_CAMERA_SIGNAL_DISABLED 0 + +static bool early_camera_clock_off; static struct msm_sensor_init_t *s_init; static int msm_sensor_wait_for_probe_done(struct msm_sensor_init_t *s_init) @@ -42,10 +48,14 @@ static int msm_sensor_wait_for_probe_done(struct msm_sensor_init_t *s_init) return rc; } +#define MMSS_A_VFE_0_SPARE 0xC84 + /* Static function definition */ int32_t msm_sensor_driver_cmd(struct msm_sensor_init_t *s_init, void *arg) { int32_t rc = 0; + u32 val = 0; + void __iomem *base; struct sensor_init_cfg_data *cfg = (struct sensor_init_cfg_data *)arg; /* Validate input parameters */ @@ -68,6 +78,28 @@ int32_t msm_sensor_driver_cmd(struct msm_sensor_init_t *s_init, void *arg) break; case CFG_SINIT_PROBE_DONE: + if (early_camera_clock_off == false) { + base = ioremap(0x00A10000, 0x1000); + val = msm_camera_io_r_mb(base + MMSS_A_VFE_0_SPARE); + while (val != EARLY_CAMERA_SIGNAL_DONE) { + if (val == EARLY_CAMERA_SIGNAL_DISABLED) + break; + msleep(1000); + val = msm_camera_io_r_mb( + base + MMSS_A_VFE_0_SPARE); + pr_err("Waiting for signal from LK val = %u\n", + val); + } + rc = msm_early_cam_disable_clocks(); + if (rc < 0) { + pr_err("Failed to disable early camera :%d\n", + rc); + } else { + early_camera_clock_off = true; + pr_debug("Voted OFF early camera clocks\n"); + } + } + s_init->module_init_status = 1; wake_up(&s_init->state_wait); break; @@ -99,6 +131,7 @@ static int __init msm_sensor_init_module(void) mutex_init(&s_init->imutex); init_waitqueue_head(&s_init->state_wait); + early_camera_clock_off = false; return ret; } diff --git a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c index 236660dca3fb..3dccb73d9de4 100644 --- a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c +++ b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c @@ -623,11 +623,13 @@ static long msm_ois_subdev_ioctl(struct v4l2_subdev *sd, pr_err("o_ctrl->i2c_client.i2c_func_tbl NULL\n"); return -EINVAL; } + mutex_lock(o_ctrl->ois_mutex); rc = msm_ois_power_down(o_ctrl); if (rc < 0) { pr_err("%s:%d OIS Power down failed\n", __func__, __LINE__); } + mutex_unlock(o_ctrl->ois_mutex); return msm_ois_close(sd, NULL); default: return -ENOIOCTLCMD; @@ -781,6 +783,7 @@ static long msm_ois_subdev_do_ioctl( u32 = (struct msm_ois_cfg_data32 *)arg; parg = arg; + switch (cmd) { case VIDIOC_MSM_OIS_CFG32: cmd = VIDIOC_MSM_OIS_CFG; @@ -818,7 +821,6 @@ static long msm_ois_subdev_do_ioctl( settings.reg_setting = compat_ptr(settings32.reg_setting); - ois_data.cfgtype = u32->cfgtype; ois_data.cfg.settings = &settings; parg = &ois_data; break; @@ -826,6 +828,10 @@ static long msm_ois_subdev_do_ioctl( parg = &ois_data; break; } + break; + case VIDIOC_MSM_OIS_CFG: + pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd); + return -EINVAL; } rc = msm_ois_subdev_ioctl(sd, cmd, parg); diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index df0664b496ba..aeeb5cae3096 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -623,6 +623,7 @@ static int camera_v4l2_open(struct file *filep) unsigned long opn_idx, idx; BUG_ON(!pvdev); + mutex_lock(&pvdev->video_drvdata_mutex); rc = camera_v4l2_fh_open(filep); if (rc < 0) { pr_err("%s : camera_v4l2_fh_open failed Line %d rc %d\n", @@ -693,6 +694,7 @@ static int camera_v4l2_open(struct file *filep) idx |= (1 << find_first_zero_bit((const unsigned long *)&opn_idx, MSM_CAMERA_STREAM_CNT_BITS)); atomic_cmpxchg(&pvdev->opened, opn_idx, idx); + mutex_unlock(&pvdev->video_drvdata_mutex); return rc; @@ -707,6 +709,7 @@ stream_fail: vb2_q_fail: camera_v4l2_fh_release(filep); fh_open_fail: + mutex_unlock(&pvdev->video_drvdata_mutex); return rc; } @@ -737,6 +740,7 @@ static int camera_v4l2_close(struct file *filep) if (WARN_ON(!session)) return -EIO; + mutex_lock(&pvdev->video_drvdata_mutex); mutex_lock(&session->close_lock); opn_idx = atomic_read(&pvdev->opened); mask = (1 << sp->stream_id); @@ -778,6 +782,7 @@ static int camera_v4l2_close(struct file *filep) } camera_v4l2_fh_release(filep); + mutex_unlock(&pvdev->video_drvdata_mutex); return 0; } @@ -924,6 +929,7 @@ int camera_init_v4l2(struct device *dev, unsigned int *session) *session = pvdev->vdev->num; atomic_set(&pvdev->opened, 0); + mutex_init(&pvdev->video_drvdata_mutex); video_set_drvdata(pvdev->vdev, pvdev); device_init_wakeup(&pvdev->vdev->dev, 1); goto init_end; diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c index c243d587e308..f6d7f5fb8d32 100644 --- a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c +++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2014, The Linux Foundataion. All rights reserved. +/* Copyright (c) 2011-2014, 2017, The Linux Foundataion. 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 @@ -175,35 +175,45 @@ int32_t msm_camera_io_poll_value_wmask(void __iomem *addr, u32 wait_data, void msm_camera_io_dump(void __iomem *addr, int size, int enable) { - char line_str[128], *p_str; + char line_str[128]; int i; - u32 *p = (u32 *) addr; + ptrdiff_t p = 0; + size_t offset = 0, used = 0; u32 data; CDBG("%s: addr=%pK size=%d\n", __func__, addr, size); - if (!p || (size <= 0) || !enable) + if (!addr || (size <= 0) || !enable) return; line_str[0] = '\0'; - p_str = line_str; for (i = 0; i < size/4; i++) { if (i % 4 == 0) { -#ifdef CONFIG_COMPAT - snprintf(p_str, 20, "%016lx: ", (unsigned long) p); - p_str += 18; -#else - snprintf(p_str, 12, "%08lx: ", (unsigned long) p); - p_str += 10; -#endif + used = snprintf(line_str + offset, + sizeof(line_str) - offset, "0x%04tX: ", p); + if (offset + used >= sizeof(line_str)) { + pr_err("%s\n", line_str); + offset = 0; + line_str[0] = '\0'; + } else { + offset += used; + } + } + data = readl_relaxed(addr + p); + p = p + 4; + used = snprintf(line_str + offset, + sizeof(line_str) - offset, "%08x ", data); + if (offset + used >= sizeof(line_str)) { + pr_err("%s\n", line_str); + offset = 0; + line_str[0] = '\0'; + } else { + offset += used; } - data = readl_relaxed(p++); - snprintf(p_str, 12, "%08x ", data); - p_str += 9; if ((i + 1) % 4 == 0) { pr_err("%s\n", line_str); line_str[0] = '\0'; - p_str = line_str; + offset = 0; } } if (line_str[0] != '\0') @@ -546,12 +556,16 @@ int msm_camera_enable_vreg(struct device *dev, struct camera_vreg_t *cam_vreg, continue; } else j = i; - regulator_disable(reg_ptr[j]); - if (cam_vreg[j].delay > 20) - msleep(cam_vreg[j].delay); - else if (cam_vreg[j].delay) - usleep_range(cam_vreg[j].delay * 1000, - (cam_vreg[j].delay * 1000) + 1000); + if (reg_ptr[j]) { + regulator_disable(reg_ptr[j]); + if (cam_vreg[j].delay > 20) + msleep(cam_vreg[j].delay); + else if (cam_vreg[j].delay) + usleep_range( + cam_vreg[j].delay * 1000, + (cam_vreg[j].delay * 1000) + + 1000); + } } } return rc; diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c index a04d7ca73fe1..d881b4aea48f 100644 --- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c +++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c @@ -434,6 +434,7 @@ static int msm_fd_open(struct file *file) ctx->vb2_q.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ctx->vb2_q.io_modes = VB2_USERPTR; ctx->vb2_q.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + mutex_init(&ctx->lock); ret = vb2_queue_init(&ctx->vb2_q); if (ret < 0) { dev_err(device->dev, "Error queue init\n"); @@ -484,7 +485,9 @@ static int msm_fd_release(struct file *file) msm_cpp_vbif_register_error_handler((void *)ctx, VBIF_CLIENT_FD, NULL); + mutex_lock(&ctx->lock); vb2_queue_release(&ctx->vb2_q); + mutex_unlock(&ctx->lock); vfree(ctx->stats); @@ -514,7 +517,9 @@ static unsigned int msm_fd_poll(struct file *file, struct fd_ctx *ctx = msm_fd_ctx_from_fh(file->private_data); unsigned int ret; + mutex_lock(&ctx->lock); ret = vb2_poll(&ctx->vb2_q, file, wait); + mutex_unlock(&ctx->lock); if (atomic_read(&ctx->subscribed_for_event)) { poll_wait(file, &ctx->fh.wait, wait); @@ -752,9 +757,9 @@ static int msm_fd_reqbufs(struct file *file, int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - mutex_lock(&ctx->fd_device->recovery_lock); + mutex_lock(&ctx->lock); ret = vb2_reqbufs(&ctx->vb2_q, req); - mutex_unlock(&ctx->fd_device->recovery_lock); + mutex_unlock(&ctx->lock); return ret; } @@ -770,9 +775,9 @@ static int msm_fd_qbuf(struct file *file, void *fh, int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - mutex_lock(&ctx->fd_device->recovery_lock); + mutex_lock(&ctx->lock); ret = vb2_qbuf(&ctx->vb2_q, pb); - mutex_unlock(&ctx->fd_device->recovery_lock); + mutex_unlock(&ctx->lock); return ret; } @@ -789,9 +794,9 @@ static int msm_fd_dqbuf(struct file *file, int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - mutex_lock(&ctx->fd_device->recovery_lock); + mutex_lock(&ctx->lock); ret = vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK); - mutex_unlock(&ctx->fd_device->recovery_lock); + mutex_unlock(&ctx->lock); return ret; } @@ -807,7 +812,9 @@ static int msm_fd_streamon(struct file *file, struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); int ret; + mutex_lock(&ctx->lock); ret = vb2_streamon(&ctx->vb2_q, buf_type); + mutex_unlock(&ctx->lock); if (ret < 0) dev_err(ctx->fd_device->dev, "Stream on fails\n"); @@ -826,7 +833,9 @@ static int msm_fd_streamoff(struct file *file, struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); int ret; + mutex_lock(&ctx->lock); ret = vb2_streamoff(&ctx->vb2_q, buf_type); + mutex_unlock(&ctx->lock); if (ret < 0) dev_err(ctx->fd_device->dev, "Stream off fails\n"); @@ -1057,14 +1066,18 @@ static int msm_fd_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) a->value = ctx->format.size->work_size; break; case V4L2_CID_FD_WORK_MEMORY_FD: + mutex_lock(&ctx->fd_device->recovery_lock); if (ctx->work_buf.fd != -1) msm_fd_hw_unmap_buffer(&ctx->work_buf); if (a->value >= 0) { ret = msm_fd_hw_map_buffer(&ctx->mem_pool, a->value, &ctx->work_buf); - if (ret < 0) + if (ret < 0) { + mutex_unlock(&ctx->fd_device->recovery_lock); return ret; + } } + mutex_unlock(&ctx->fd_device->recovery_lock); break; default: return -EINVAL; diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h index 6eae2b8d56fb..2b81e5b9ece3 100644 --- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h +++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -174,6 +174,7 @@ struct fd_ctx { struct msm_fd_mem_pool mem_pool; struct msm_fd_stats *stats; struct msm_fd_buf_handle work_buf; + struct mutex lock; }; /* diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index 1f1435fe282e..b2d152bf4ef0 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -786,6 +786,7 @@ struct vfe_device { size_t num_norm_clk; bool hvx_clk_state; enum cam_ahb_clk_vote ahb_vote; + enum cam_ahb_clk_vote user_requested_ahb_vote; struct cx_ipeak_client *vfe_cx_ipeak; /* Sync variables*/ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index 6515e3d6ecbc..24d1c6cba84d 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -274,10 +274,12 @@ int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev, enum cam_ahb_clk_vote src_clk_vote; struct msm_isp_clk_rates clk_rates; - if (ahb_cfg) + if (ahb_cfg) { vote = msm_isp47_get_cam_clk_vote(ahb_cfg->vote); - else - vote = CAM_AHB_SVS_VOTE; + vfe_dev->user_requested_ahb_vote = vote; + } else { + vote = vfe_dev->user_requested_ahb_vote; + } vfe_dev->hw_info->vfe_ops.platform_ops.get_clk_rates(vfe_dev, &clk_rates); @@ -327,6 +329,7 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev) if (rc) goto clk_enable_failed; + vfe_dev->user_requested_ahb_vote = CAM_AHB_SVS_VOTE; rc = cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE); if (rc < 0) { pr_err("%s: failed to vote for AHB\n", __func__); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h index a8d4cfb43927..0f029c0d5178 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h @@ -136,7 +136,7 @@ static inline void msm_isp_cfg_stream_scratch( } static inline struct msm_vfe_axi_stream *msm_isp_get_stream_common_data( - struct vfe_device *vfe_dev, int stream_idx) + struct vfe_device *vfe_dev, uint32_t stream_idx) { struct msm_vfe_common_dev_data *common_data = vfe_dev->common_data; struct msm_vfe_axi_stream *stream_info; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index e87f2414a879..92b1f2ea871b 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -196,7 +196,7 @@ uint32_t msm_isp_get_framedrop_period( return 32; break; case SKIP_ALL: - return 1; + return SKIP_ALL; default: return 1; } diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 3f900ded090a..9c3bd7b41ce9 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -1025,6 +1025,9 @@ static void msm_ispif_config_stereo(struct ispif_device *ispif, enum msm_ispif_vfe_intf vfe_intf; uint32_t stereo_3d_threshold = STEREO_DEFAULT_3D_THRESHOLD; + if (params->num > MAX_PARAM_ENTRIES) + return; + for (i = 0; i < params->num; i++) { vfe_intf = params->entries[i].vfe_intf; if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) { diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c index e40869d41a5d..821833d53905 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -903,26 +903,45 @@ int msm_jpeg_hw_exec_cmds(struct msm_jpeg_hw_cmd *hw_cmd_p, uint32_t m_cmds, void msm_jpeg_io_dump(void *base, int size) { - char line_str[128], *p_str; + char line_str[128]; void __iomem *addr = (void __iomem *)base; int i; u32 *p = (u32 *) addr; + size_t offset = 0; + size_t used = 0; + size_t min_range = 0; + size_t sizeof_line_str = sizeof(line_str); u32 data; JPEG_DBG_HIGH("%s:%d] %pK %d", __func__, __LINE__, addr, size); line_str[0] = '\0'; - p_str = line_str; for (i = 0; i < size/4; i++) { if (i % 4 == 0) { - snprintf(p_str, 12, "%08lx: ", (unsigned long)p); - p_str += 10; + used = snprintf(line_str + offset, + sizeof_line_str - offset, "%pK ", p); + if ((used < min_range) || + (offset + used >= sizeof_line_str)) { + JPEG_PR_ERR("%s\n", line_str); + offset = 0; + line_str[0] = '\0'; + } else { + offset += used; + } } data = msm_camera_io_r(p++); - snprintf(p_str, 12, "%08x ", data); - p_str += 9; + used = snprintf(line_str + offset, + sizeof_line_str - offset, "%08x ", data); + if ((used < min_range) || + (offset + used >= sizeof_line_str)) { + JPEG_PR_ERR("%s\n", line_str); + offset = 0; + line_str[0] = '\0'; + } else { + offset += used; + } if ((i + 1) % 4 == 0) { JPEG_DBG_HIGH("%s\n", line_str); line_str[0] = '\0'; - p_str = line_str; + offset = 0; } } if (line_str[0] != '\0') diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index 9cb7d5299ef8..7cfeffc92d48 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -34,6 +34,7 @@ static struct v4l2_device *msm_v4l2_dev; static struct list_head ordered_sd_list; +static struct mutex ordered_sd_mtx; static struct pm_qos_request msm_v4l2_pm_qos_request; @@ -287,6 +288,7 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id) return; while (1) { + unsigned long wl_flags; if (try_count > 5) { pr_err("%s : not able to delete stream %d\n", @@ -294,18 +296,20 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id) break; } - write_lock(&session->stream_rwlock); + write_lock_irqsave(&session->stream_rwlock, wl_flags); try_count++; stream = msm_queue_find(&session->stream_q, struct msm_stream, list, __msm_queue_find_stream, &stream_id); if (!stream) { - write_unlock(&session->stream_rwlock); + write_unlock_irqrestore(&session->stream_rwlock, + wl_flags); return; } if (msm_vb2_get_stream_state(stream) != 1) { - write_unlock(&session->stream_rwlock); + write_unlock_irqrestore(&session->stream_rwlock, + wl_flags); continue; } @@ -315,7 +319,7 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id) kfree(stream); stream = NULL; spin_unlock_irqrestore(&(session->stream_q.lock), flags); - write_unlock(&session->stream_rwlock); + write_unlock_irqrestore(&session->stream_rwlock, wl_flags); break; } @@ -385,6 +389,11 @@ static void msm_add_sd_in_position(struct msm_sd_subdev *msm_subdev, struct msm_sd_subdev *temp_sd; list_for_each_entry(temp_sd, sd_list, list) { + if (temp_sd == msm_subdev) { + pr_err("%s :Fail to add the same sd %d\n", + __func__, __LINE__); + return; + } if (msm_subdev->close_seq < temp_sd->close_seq) { list_add_tail(&msm_subdev->list, &temp_sd->list); return; @@ -401,7 +410,9 @@ int msm_sd_register(struct msm_sd_subdev *msm_subdev) if (WARN_ON(!msm_v4l2_dev) || WARN_ON(!msm_v4l2_dev->dev)) return -EIO; + mutex_lock(&ordered_sd_mtx); msm_add_sd_in_position(msm_subdev, &ordered_sd_list); + mutex_unlock(&ordered_sd_mtx); return __msm_sd_register_subdev(&msm_subdev->sd); } EXPORT_SYMBOL(msm_sd_register); @@ -803,11 +814,13 @@ static long msm_private_ioctl(struct file *file, void *fh, __func__); } + mutex_lock(&ordered_sd_mtx); if (!list_empty(&msm_v4l2_dev->subdevs)) { list_for_each_entry(msm_sd, &ordered_sd_list, list) __msm_sd_notify_freeze_subdevs(msm_sd, event_data->status); } + mutex_unlock(&ordered_sd_mtx); } break; @@ -992,9 +1005,11 @@ static int msm_close(struct file *filep) struct msm_sd_subdev *msm_sd; /*stop all hardware blocks immediately*/ + mutex_lock(&ordered_sd_mtx); if (!list_empty(&msm_v4l2_dev->subdevs)) list_for_each_entry(msm_sd, &ordered_sd_list, list) __msm_sd_close_subdevs(msm_sd, &sd_close); + mutex_unlock(&ordered_sd_mtx); /* remove msm_v4l2_pm_qos_request */ msm_pm_qos_remove_request(); @@ -1350,6 +1365,7 @@ static int msm_probe(struct platform_device *pdev) msm_init_queue(msm_session_q); spin_lock_init(&msm_eventq_lock); spin_lock_init(&msm_pid_lock); + mutex_init(&ordered_sd_mtx); INIT_LIST_HEAD(&ordered_sd_list); cam_debugfs_root = debugfs_create_dir(MSM_CAM_LOGSYNC_FILE_BASEDIR, diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h index dce47bc7249c..8bdb14f5c16e 100644 --- a/drivers/media/platform/msm/camera_v2/msm.h +++ b/drivers/media/platform/msm/camera_v2/msm.h @@ -46,6 +46,7 @@ extern bool is_daemon_status; struct msm_video_device { struct video_device *vdev; atomic_t opened; + struct mutex video_drvdata_mutex; }; struct msm_queue_head { diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c index 719b14226067..e271c7fcd1b6 100644 --- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c +++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c @@ -47,22 +47,23 @@ int msm_vb2_buf_init(struct vb2_buffer *vb) struct msm_session *session; struct msm_vb2_buffer *msm_vb2_buf; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + unsigned long rl_flags; session = msm_get_session_from_vb2q(vb->vb2_queue); if (IS_ERR_OR_NULL(session)) return -EINVAL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s: Couldn't find stream\n", __func__); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } msm_vb2_buf = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf); msm_vb2_buf->in_freeq = 0; - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return 0; } @@ -71,7 +72,7 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb) struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct msm_session *session; - unsigned long flags; + unsigned long flags, rl_flags; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); msm_vb2 = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf); @@ -84,19 +85,19 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb) if (IS_ERR_OR_NULL(session)) return; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s:%d] NULL stream", __func__, __LINE__); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return; } spin_lock_irqsave(&stream->stream_lock, flags); list_add_tail(&msm_vb2->list, &stream->queued_list); spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); } static void msm_vb2_buf_finish(struct vb2_buffer *vb) @@ -104,26 +105,26 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb) struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct msm_session *session; - unsigned long flags; + unsigned long flags, rl_flags; struct msm_vb2_buffer *msm_vb2_entry, *temp; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); msm_vb2 = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf); if (!msm_vb2) { pr_err("%s:%d] vb2_buf NULL", __func__, __LINE__); - return; + return; } session = msm_get_session_from_vb2q(vb->vb2_queue); if (IS_ERR_OR_NULL(session)) return; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s:%d] NULL stream", __func__, __LINE__); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return; } @@ -136,7 +137,7 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb) } } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return; } @@ -145,19 +146,19 @@ static void msm_vb2_stop_stream(struct vb2_queue *q) struct msm_vb2_buffer *msm_vb2, *temp; struct msm_stream *stream; struct msm_session *session; - unsigned long flags; + unsigned long flags, rl_flags; struct vb2_v4l2_buffer *vb2_v4l2_buf; session = msm_get_session_from_vb2q(q); if (IS_ERR_OR_NULL(session)) return; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream_from_vb2q(q); if (!stream) { pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return; } @@ -177,7 +178,7 @@ static void msm_vb2_stop_stream(struct vb2_queue *q) msm_vb2->in_freeq = 0; } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); } int msm_vb2_get_stream_state(struct msm_stream *stream) @@ -255,17 +256,17 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id, struct msm_session *session; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; struct msm_vb2_buffer *msm_vb2 = NULL; - unsigned long flags; + unsigned long flags, rl_flags; session = msm_get_session(session_id); if (IS_ERR_OR_NULL(session)) return NULL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return NULL; } @@ -291,7 +292,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id, vb2_v4l2_buf = NULL; end: spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return vb2_v4l2_buf; } @@ -302,18 +303,18 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id, struct msm_session *session; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; struct msm_vb2_buffer *msm_vb2 = NULL; - unsigned long flags; + unsigned long flags, rl_flags; session = msm_get_session(session_id); if (IS_ERR_OR_NULL(session)) return NULL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return NULL; } @@ -337,7 +338,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id, vb2_v4l2_buf = NULL; end: spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return vb2_v4l2_buf; } @@ -349,17 +350,17 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, struct msm_vb2_buffer *msm_vb2; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; int rc = 0; - unsigned long flags; + unsigned long flags, rl_flags; session = msm_get_session(session_id); if (IS_ERR_OR_NULL(session)) return -EINVAL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } @@ -374,7 +375,8 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, pr_err("VB buffer is INVALID vb=%pK, ses_id=%d, str_id=%d\n", vb, session_id, stream_id); spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, + rl_flags); return -EINVAL; } msm_vb2 = @@ -391,7 +393,7 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, rc = -EINVAL; } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return rc; } @@ -399,7 +401,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, unsigned int stream_id, uint32_t sequence, struct timeval *ts, uint32_t reserved) { - unsigned long flags; + unsigned long flags, rl_flags; struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct msm_session *session; @@ -410,11 +412,11 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, if (IS_ERR_OR_NULL(session)) return -EINVAL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } @@ -429,7 +431,8 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, pr_err("VB buffer is INVALID ses_id=%d, str_id=%d, vb=%pK\n", session_id, stream_id, vb); spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, + rl_flags); return -EINVAL; } msm_vb2 = @@ -450,7 +453,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, rc = -EINVAL; } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return rc; } @@ -461,18 +464,18 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, struct msm_session *session; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; struct msm_vb2_buffer *msm_vb2 = NULL; - unsigned long flags; + unsigned long flags, rl_flags; long rc = -EINVAL; session = msm_get_session(session_id); if (IS_ERR_OR_NULL(session)) return rc; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } @@ -501,14 +504,14 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, end: spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return rc; } EXPORT_SYMBOL(msm_vb2_return_buf_by_idx); static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) { - unsigned long flags; + unsigned long flags, rl_flags; struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct msm_session *session; @@ -518,11 +521,11 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) if (IS_ERR_OR_NULL(session)) return -EINVAL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } @@ -534,7 +537,7 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) msm_vb2->in_freeq = 0; } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return 0; } diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index bc844bb87db5..b1bea12c2cc3 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -1707,6 +1707,10 @@ static long msm_actuator_subdev_do_ioctl( parg = &actuator_data; break; } + break; + case VIDIOC_MSM_ACTUATOR_CFG: + pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd); + return -EINVAL; } rc = msm_actuator_subdev_ioctl(sd, cmd, parg); diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index 7dda92510879..3cb6b55ccc8c 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -403,6 +403,10 @@ static int32_t msm_cci_calc_cmd_len(struct cci_device *cci_dev, if (cmd->reg_addr + 1 == (cmd+1)->reg_addr) { len += data_len; + if (len > cci_dev->payload_size) { + len = len - data_len; + break; + } *pack += data_len; } else break; diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c index cf7d1a8aa1f4..223ddf39dce8 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c @@ -508,18 +508,42 @@ static int32_t msm_flash_init( return 0; } -#ifdef CONFIG_COMPAT static int32_t msm_flash_init_prepare( struct msm_flash_ctrl_t *flash_ctrl, struct msm_flash_cfg_data_t *flash_data) { +#ifdef CONFIG_COMPAT + struct msm_flash_cfg_data_t flash_data_k; + struct msm_flash_init_info_t flash_init_info; + int32_t i = 0; + + if (!is_compat_task()) { + /*for 64-bit usecase,it need copy the data to local memory*/ + flash_data_k.cfg_type = flash_data->cfg_type; + for (i = 0; i < MAX_LED_TRIGGERS; i++) { + flash_data_k.flash_current[i] = + flash_data->flash_current[i]; + flash_data_k.flash_duration[i] = + flash_data->flash_duration[i]; + } + + flash_data_k.cfg.flash_init_info = &flash_init_info; + if (copy_from_user(&flash_init_info, + (void __user *)(flash_data->cfg.flash_init_info), + sizeof(struct msm_flash_init_info_t))) { + pr_err("%s copy_from_user failed %d\n", + __func__, __LINE__); + return -EFAULT; + } + return msm_flash_init(flash_ctrl, &flash_data_k); + } + /* + * for 32-bit usecase,it already copy the userspace + * data to local memory in msm_flash_subdev_do_ioctl() + * so here do not need copy from user + */ return msm_flash_init(flash_ctrl, flash_data); -} #else -static int32_t msm_flash_init_prepare( - struct msm_flash_ctrl_t *flash_ctrl, - struct msm_flash_cfg_data_t *flash_data) -{ struct msm_flash_cfg_data_t flash_data_k; struct msm_flash_init_info_t flash_init_info; int32_t i = 0; @@ -534,15 +558,15 @@ static int32_t msm_flash_init_prepare( flash_data_k.cfg.flash_init_info = &flash_init_info; if (copy_from_user(&flash_init_info, - (void *)(flash_data->cfg.flash_init_info), + (void __user *)(flash_data->cfg.flash_init_info), sizeof(struct msm_flash_init_info_t))) { pr_err("%s copy_from_user failed %d\n", __func__, __LINE__); return -EFAULT; } return msm_flash_init(flash_ctrl, &flash_data_k); -} #endif +} static int32_t msm_flash_prepare( struct msm_flash_ctrl_t *flash_ctrl) diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c index 1a40aa1c78bd..6d9b0e987d0d 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,13 +17,15 @@ #undef CDBG #define CDBG(fmt, args...) pr_debug(fmt, ##args) #define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args) +#define MAX_I2C_ADDR_TYPE_SIZE (MSM_CAMERA_I2C_3B_ADDR + 1) +#define MAX_I2C_DATA_TYPE_SIZE (MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA + 1) int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client, uint32_t addr, uint16_t *data, enum msm_camera_i2c_data_type data_type) { int32_t rc = -EFAULT; - unsigned char buf[client->addr_type+data_type]; + unsigned char buf[MAX_I2C_ADDR_TYPE_SIZE + MAX_I2C_DATA_TYPE_SIZE]; struct msm_camera_cci_ctrl cci_ctrl; if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c index 3f079fe2c173..457bd1730232 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -575,6 +575,8 @@ int msm_camera_get_dt_power_setting_data(struct device_node *of_node, ps[i].seq_val = SENSOR_GPIO_CUSTOM1; else if (!strcmp(seq_name, "sensor_gpio_custom2")) ps[i].seq_val = SENSOR_GPIO_CUSTOM2; + else if (!strcmp(seq_name, "sensor_gpio_custom3")) + ps[i].seq_val = SENSOR_GPIO_CUSTOM3; else rc = -EILSEQ; break; @@ -1078,6 +1080,27 @@ int msm_camera_init_gpio_pin_tbl(struct device_node *of_node, rc = 0; } + rc = of_property_read_u32(of_node, "qcom,gpio-custom3", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-custom3 failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-custom3 invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM3] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_CUSTOM3] = 1; + CDBG("%s qcom,gpio-custom3 %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM3]); + } else { + rc = 0; + } + return rc; ERROR: diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c index a41d7dba490e..57bc392f54fd 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -108,7 +108,11 @@ static int32_t msm_sensor_driver_create_i2c_v4l_subdev s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name; s_ctrl->sensordata->sensor_info->session_id = session_id; s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3; - msm_sd_register(&s_ctrl->msm_sd); + rc = msm_sd_register(&s_ctrl->msm_sd); + if (rc < 0) { + pr_err("failed: msm_sd_register rc %d", rc); + return rc; + } msm_sensor_v4l2_subdev_fops = v4l2_subdev_fops; #ifdef CONFIG_COMPAT msm_sensor_v4l2_subdev_fops.compat_ioctl32 = @@ -148,7 +152,11 @@ static int32_t msm_sensor_driver_create_v4l_subdev s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR; s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name; s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3; - msm_sd_register(&s_ctrl->msm_sd); + rc = msm_sd_register(&s_ctrl->msm_sd); + if (rc < 0) { + pr_err("failed: msm_sd_register rc %d", rc); + return rc; + } msm_cam_copy_v4l2_subdev_fops(&msm_sensor_v4l2_subdev_fops); #ifdef CONFIG_COMPAT msm_sensor_v4l2_subdev_fops.compat_ioctl32 = @@ -995,12 +1003,6 @@ CSID_TG: pr_err("%s probe succeeded", slave_info->sensor_name); - /* - Set probe succeeded flag to 1 so that no other camera shall - * probed on this slot - */ - s_ctrl->is_probe_succeed = 1; - s_ctrl->bypass_video_node_creation = slave_info->bypass_video_node_creation; @@ -1048,6 +1050,11 @@ CSID_TG: msm_sensor_fill_sensor_info(s_ctrl, probed_info, entity_name); + /* + * Set probe succeeded flag to 1 so that no other camera shall + * probed on this slot + */ + s_ctrl->is_probe_succeed = 1; return rc; camera_power_down: diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c index bfb15846e73c..302a7b16bc26 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c +++ b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c @@ -615,11 +615,13 @@ static long msm_ois_subdev_ioctl(struct v4l2_subdev *sd, pr_err("o_ctrl->i2c_client.i2c_func_tbl NULL\n"); return -EINVAL; } + mutex_lock(o_ctrl->ois_mutex); rc = msm_ois_power_down(o_ctrl); if (rc < 0) { pr_err("%s:%d OIS Power down failed\n", __func__, __LINE__); } + mutex_unlock(o_ctrl->ois_mutex); return msm_ois_close(sd, NULL); default: return -ENOIOCTLCMD; @@ -818,6 +820,10 @@ static long msm_ois_subdev_do_ioctl( parg = &ois_data; break; } + break; + case VIDIOC_MSM_OIS_CFG: + pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd); + return -EINVAL; } rc = msm_ois_subdev_ioctl(sd, cmd, parg); diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 28a4006b480c..0f6389370643 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -1491,6 +1491,7 @@ static int msm_vdec_queue_setup(struct vb2_queue *q, rc = -EINVAL; break; } + msm_dcvs_try_enable(inst); /* Pretend as if FW itself is asking for * additional buffers. @@ -1570,9 +1571,10 @@ exit: return rc; } -static inline int set_max_internal_buffers_size(struct msm_vidc_inst *inst) +static int set_max_internal_buffers_size(struct msm_vidc_inst *inst) { int rc = 0; + struct msm_vidc_list *buf_list = &inst->scratchbufs; struct { enum hal_buffer type; struct hal_buffer_requirements *req; @@ -1580,13 +1582,17 @@ static inline int set_max_internal_buffers_size(struct msm_vidc_inst *inst) } internal_buffers[] = { { HAL_BUFFER_INTERNAL_SCRATCH, NULL, 0}, { HAL_BUFFER_INTERNAL_SCRATCH_1, NULL, 0}, - { HAL_BUFFER_INTERNAL_SCRATCH_2, NULL, 0}, - { HAL_BUFFER_INTERNAL_PERSIST, NULL, 0}, - { HAL_BUFFER_INTERNAL_PERSIST_1, NULL, 0}, }; struct hal_frame_size frame_sz; int i; + mutex_lock(&buf_list->lock); + if (!list_empty(&buf_list->list)) { + dprintk(VIDC_DBG, "Scratch list already has allocated buf\n"); + mutex_unlock(&buf_list->lock); + return 0; + } + mutex_unlock(&buf_list->lock); frame_sz.buffer_type = HAL_BUFFER_INPUT; frame_sz.width = inst->capability.width.max; @@ -1612,6 +1618,15 @@ static inline int set_max_internal_buffers_size(struct msm_vidc_inst *inst) get_buff_req_buffer(inst, internal_buffers[i].type); internal_buffers[i].size = internal_buffers[i].req ? internal_buffers[i].req->buffer_size : 0; + + rc = allocate_and_set_internal_bufs(inst, + internal_buffers[i].req, + &inst->scratchbufs, false); + if (rc) + goto alloc_fail; + dprintk(VIDC_DBG, + "Allocated scratch type : %d size to : %zd\n", + internal_buffers[i].type, internal_buffers[i].size); } frame_sz.buffer_type = HAL_BUFFER_INPUT; @@ -1624,25 +1639,18 @@ static inline int set_max_internal_buffers_size(struct msm_vidc_inst *inst) dprintk(VIDC_ERR, "%s Failed to get back old buf req, %d\n", __func__, rc); - return rc; + goto alloc_fail; } - dprintk(VIDC_DBG, "Old buffer reqs, buffer type = %d width = %d, height = %d\n", frame_sz.buffer_type, frame_sz.width, frame_sz.height); - for (i = 0; i < ARRAY_SIZE(internal_buffers); i++) { - if (internal_buffers[i].req) { - internal_buffers[i].req->buffer_size = - internal_buffers[i].size; - dprintk(VIDC_DBG, - "Changing buffer type : %d size to : %zd\n", - internal_buffers[i].type, - internal_buffers[i].size); - } - } return 0; + +alloc_fail: + msm_comm_release_scratch_buffers(inst, false); + return rc; } static inline int start_streaming(struct msm_vidc_inst *inst) @@ -1653,6 +1661,7 @@ static inline int start_streaming(struct msm_vidc_inst *inst) struct hal_buffer_size_minimum b; unsigned int buffer_size; struct msm_vidc_format *fmt = NULL; + bool max_internal_buf = false; fmt = &inst->fmts[CAPTURE_PORT]; buffer_size = fmt->get_frame_size(0, @@ -1676,8 +1685,9 @@ static inline int start_streaming(struct msm_vidc_inst *inst) dprintk(VIDC_ERR, "H/w scaling is not in valid range\n"); return -EINVAL; } - if ((inst->flags & VIDC_SECURE) && !inst->in_reconfig && - !slave_side_cp) { + max_internal_buf = (inst->flags & VIDC_SECURE) && !slave_side_cp + && (inst->session_type == MSM_VIDC_DECODER); + if (max_internal_buf) { rc = set_max_internal_buffers_size(inst); if (rc) { dprintk(VIDC_ERR, @@ -1686,7 +1696,7 @@ static inline int start_streaming(struct msm_vidc_inst *inst) goto fail_start; } } - rc = msm_comm_set_scratch_buffers(inst); + rc = msm_comm_set_scratch_buffers(inst, max_internal_buf); if (rc) { dprintk(VIDC_ERR, "Failed to set scratch buffers: %d\n", rc); diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index c5816511e056..e4698e0cdcd8 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -1868,7 +1868,7 @@ static inline int start_streaming(struct msm_vidc_inst *inst) "Failed to get Buffer Requirements : %d\n", rc); goto fail_start; } - rc = msm_comm_set_scratch_buffers(inst); + rc = msm_comm_set_scratch_buffers(inst, false); if (rc) { dprintk(VIDC_ERR, "Failed to set scratch buffers: %d\n", rc); goto fail_start; diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index 2b3070974df8..3677bb6e32e6 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -533,11 +533,18 @@ static inline void save_v4l2_buffer(struct v4l2_buffer *b, static int __map_and_update_binfo(struct msm_vidc_inst *inst, struct buffer_info *binfo, - struct v4l2_buffer *b, int i) + struct v4l2_buffer *b, u32 i) { int rc = 0; struct msm_smem *same_fd_handle = NULL; + if (i >= VIDEO_MAX_PLANES) { + dprintk(VIDC_ERR, "Num planes exceeds max: %d, %d\n", + i, VIDEO_MAX_PLANES); + rc = -EINVAL; + goto exit; + } + same_fd_handle = get_same_fd_buffer( inst, b->m.planes[i].reserved[0]); @@ -558,6 +565,7 @@ static int __map_and_update_binfo(struct msm_vidc_inst *inst, b->m.planes[i].m.userptr = binfo->device_addr[i]; } +exit: return rc; } @@ -565,7 +573,8 @@ static int __handle_fw_referenced_buffers(struct msm_vidc_inst *inst, struct buffer_info *binfo, struct v4l2_buffer *b) { - int i = 0, rc = 0; + int rc = 0; + u32 i = 0; if (EXTRADATA_IDX(b->length)) { i = EXTRADATA_IDX(b->length); @@ -583,8 +592,8 @@ int map_and_register_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) { struct buffer_info *binfo = NULL; struct buffer_info *temp = NULL, *iterator = NULL; - int plane = 0; - int i = 0, rc = 0; + int plane = 0, rc = 0; + u32 i = 0; if (!b || !inst) { dprintk(VIDC_ERR, "%s: invalid input\n", __func__); @@ -826,7 +835,8 @@ int output_buffer_cache_invalidate(struct msm_vidc_inst *inst, if (inst->session_type == MSM_VIDC_ENCODER && !i) - size = b->m.planes[i].bytesused; + size = b->m.planes[i].bytesused + + b->m.planes[i].data_offset; else size = -1; @@ -1060,7 +1070,8 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b) if (binfo->handle[i] && (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) { if (inst->session_type == MSM_VIDC_DECODER && !i) - size = b->m.planes[i].bytesused; + size = b->m.planes[i].bytesused + + b->m.planes[i].data_offset; else size = -1; rc = msm_comm_smem_cache_operations(inst, diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index f85fe6fd3867..1d910f4b235c 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -3342,9 +3342,9 @@ static bool reuse_internal_buffers(struct msm_vidc_inst *inst, return reused; } -static int allocate_and_set_internal_bufs(struct msm_vidc_inst *inst, +int allocate_and_set_internal_bufs(struct msm_vidc_inst *inst, struct hal_buffer_requirements *internal_bufreq, - struct msm_vidc_list *buf_list) + struct msm_vidc_list *buf_list, bool set_on_fw) { struct msm_smem *handle; struct internal_buf *binfo; @@ -3381,11 +3381,13 @@ static int allocate_and_set_internal_bufs(struct msm_vidc_inst *inst, binfo->handle = handle; binfo->buffer_type = internal_bufreq->buffer_type; - rc = set_internal_buf_on_fw(inst, internal_bufreq->buffer_type, - handle, false); - if (rc) - goto fail_set_buffers; - + if (set_on_fw) { + rc = set_internal_buf_on_fw(inst, + internal_bufreq->buffer_type, + handle, false); + if (rc) + goto fail_set_buffers; + } mutex_lock(&buf_list->lock); list_add_tail(&binfo->list, &buf_list->list); mutex_unlock(&buf_list->lock); @@ -3426,7 +3428,7 @@ static int set_internal_buffers(struct msm_vidc_inst *inst, return 0; return allocate_and_set_internal_bufs(inst, internal_buf, - buf_list); + buf_list, true); } int msm_comm_try_state(struct msm_vidc_inst *inst, int state) @@ -3587,39 +3589,6 @@ int msm_vidc_comm_cmd(void *instance, union msm_v4l2_cmd *cmd) "Failed to flush buffers: %d\n", rc); } break; - case V4L2_DEC_QCOM_CMD_RECONFIG_HINT: - { - u32 *ptr = NULL; - struct hal_buffer_requirements *output_buf; - - rc = msm_comm_try_get_bufreqs(inst); - if (rc) { - dprintk(VIDC_ERR, - "Getting buffer requirements failed: %d\n", - rc); - break; - } - - output_buf = get_buff_req_buffer(inst, - msm_comm_get_hal_output_buffer(inst)); - if (output_buf) { - if (dec) { - ptr = (u32 *)dec->raw.data; - ptr[0] = output_buf->buffer_size; - ptr[1] = output_buf->buffer_count_actual; - dprintk(VIDC_DBG, - "Reconfig hint, size is %u, count is %u\n", - ptr[0], ptr[1]); - } else { - dprintk(VIDC_ERR, "Null decoder\n"); - } - } else { - dprintk(VIDC_DBG, - "This output buffer not required, buffer_type: %x\n", - HAL_BUFFER_OUTPUT); - } - break; - } default: dprintk(VIDC_ERR, "Unknown Command %d\n", which_cmd); rc = -ENOTSUPP; @@ -4453,15 +4422,15 @@ error: return rc; } -int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst) -{ +int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst, + bool max_int_buffer) { int rc = 0; if (!inst || !inst->core || !inst->core->device) { dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } - if (msm_comm_release_scratch_buffers(inst, true)) + if (!max_int_buffer && msm_comm_release_scratch_buffers(inst, true)) dprintk(VIDC_WARN, "Failed to release scratch buffers\n"); rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH, diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h index 7d28859c040c..5658df95db26 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h @@ -41,9 +41,13 @@ int msm_comm_try_set_prop(struct msm_vidc_inst *inst, enum hal_property ptype, void *pdata); int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype, union hal_get_property *hprop); -int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst); +int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst, + bool max_int_buffer); int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst); int msm_comm_set_output_buffers(struct msm_vidc_inst *inst); +int allocate_and_set_internal_bufs(struct msm_vidc_inst *inst, + struct hal_buffer_requirements *internal_bufreq, + struct msm_vidc_list *buf_list, bool set_on_fw); int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst); int msm_comm_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb); void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h index 3a329d989918..a1b4ad48b054 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h @@ -106,6 +106,7 @@ struct clock_info { u32 count; bool has_scaling; bool has_mem_retention; + unsigned long rate_on_enable; }; struct clock_set { diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 52b56f615da9..20f02ce46029 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -1574,6 +1574,7 @@ static int __scale_clocks(struct venus_hfi_device *device, return rc; } + static int venus_hfi_scale_clocks(void *dev, int load, struct vidc_clk_scale_data *data, unsigned long instant_bitrate) @@ -1600,6 +1601,41 @@ exit: return rc; } +static void __save_clock_rate(struct venus_hfi_device *device, bool reset) +{ + struct clock_info *cl; + + venus_hfi_for_each_clock(device, cl) { + if (cl->has_scaling) { + cl->rate_on_enable = + reset ? 0 : clk_get_rate(cl->clk); + dprintk(VIDC_PROF, "Saved clock %s rate %lu\n", + cl->name, cl->rate_on_enable); + } + } +} + +static void __restore_clock_rate(struct venus_hfi_device *device) +{ + struct clock_info *cl; + + venus_hfi_for_each_clock(device, cl) { + if (cl->has_scaling && cl->rate_on_enable) { + int rc; + + rc = __set_clk_rate(device, cl, cl->rate_on_enable); + if (rc) + dprintk(VIDC_ERR, + "Failed to restore clock %s rate %lu\n", + cl->name, cl->rate_on_enable); + else + dprintk(VIDC_DBG, + "Restored clock %s rate %lu\n", + cl->name, cl->rate_on_enable); + } + } +} + /* Writes into cmdq without raising an interrupt */ static int __iface_cmdq_write_relaxed(struct venus_hfi_device *device, void *pkt, bool *requires_interrupt) @@ -4316,6 +4352,7 @@ static inline int __suspend(struct venus_hfi_device *device) goto err_tzbsp_suspend; } + __save_clock_rate(device, false); __venus_power_off(device, true); dprintk(VIDC_PROF, "Venus power collapsed\n"); return rc; @@ -4345,6 +4382,7 @@ static inline int __resume(struct venus_hfi_device *device) dprintk(VIDC_ERR, "Failed to power on venus\n"); goto err_venus_power_on; } + __restore_clock_rate(device); /* Reboot the firmware */ rc = __tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESUME); @@ -4382,6 +4420,7 @@ exit: err_reset_core: __tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND); err_set_video_state: + __save_clock_rate(device, true); __venus_power_off(device, true); err_venus_power_on: dprintk(VIDC_ERR, "Failed to resume from power collapse\n"); @@ -4440,6 +4479,7 @@ fail_protect_mem: subsystem_put(device->resources.fw.cookie); device->resources.fw.cookie = NULL; fail_load_fw: + __save_clock_rate(device, true); __venus_power_off(device, true); fail_venus_power_on: fail_init_pkt: @@ -4461,6 +4501,7 @@ static void __unload_fw(struct venus_hfi_device *device) __vote_buses(device, NULL, 0); subsystem_put(device->resources.fw.cookie); __interface_queues_release(device); + __save_clock_rate(device, true); __venus_power_off(device, false); device->resources.fw.cookie = NULL; __deinit_resources(device); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 4a608cbe0fdb..9c6fc09b88e0 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1098,10 +1098,10 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, struct s5p_jpeg_ctx *ctx) { int c, components = 0, notfound, n_dht = 0, n_dqt = 0; - unsigned int height, width, word, subsampling = 0, sos = 0, sof = 0, - sof_len = 0; - unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER], - dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER]; + unsigned int height = 0, width = 0, word, subsampling = 0; + unsigned int sos = 0, sof = 0, sof_len = 0; + unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER]; + unsigned int dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER]; long length; struct s5p_jpeg_buffer jpeg_buffer; diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 65f80b8b9f7a..eb9e7feb9b13 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1629,7 +1629,7 @@ static void imon_incoming_packet(struct imon_context *ictx, if (kc == KEY_KEYBOARD && !ictx->release_code) { ictx->last_keycode = kc; if (!nomouse) { - ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1; + ictx->pad_mouse = !ictx->pad_mouse; dev_dbg(dev, "toggling to %s mode\n", ictx->pad_mouse ? "mouse" : "keyboard"); spin_unlock_irqrestore(&ictx->kc_lock, flags); diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 65fed7146e9b..cc91f7b3d90c 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -375,6 +375,7 @@ int enclosure_add_device(struct enclosure_device *edev, int component, struct device *dev) { struct enclosure_component *cdev; + int err; if (!edev || component >= edev->components) return -EINVAL; @@ -384,12 +385,17 @@ int enclosure_add_device(struct enclosure_device *edev, int component, if (cdev->dev == dev) return -EEXIST; - if (cdev->dev) + if (cdev->dev) { enclosure_remove_links(cdev); - - put_device(cdev->dev); + put_device(cdev->dev); + } cdev->dev = get_device(dev); - return enclosure_add_links(cdev); + err = enclosure_add_links(cdev); + if (err) { + put_device(cdev->dev); + cdev->dev = NULL; + } + return err; } EXPORT_SYMBOL_GPL(enclosure_add_device); diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c index b292ea70fb40..7fa5e326fa0b 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c @@ -1062,6 +1062,8 @@ static int audio_aio_async_write(struct q6audio_aio *audio, struct audio_client *ac; struct audio_aio_write_param param; + memset(¶m, 0, sizeof(param)); + if (!audio || !buf_node) { pr_err("%s NULL pointer audio=[0x%pK], buf_node=[0x%pK]\n", __func__, audio, buf_node); diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 7cdcd69cecf4..cf897947fff2 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -281,6 +281,7 @@ struct qseecom_control { wait_queue_head_t app_block_wq; atomic_t qseecom_state; int is_apps_region_protected; + bool smcinvoke_support; }; struct qseecom_sec_buf_fd_info { @@ -578,10 +579,12 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, desc.args[1] = req_64bit->sb_ptr; desc.args[2] = req_64bit->sb_len; } + qseecom.smcinvoke_support = true; smc_id = TZ_OS_REGISTER_LISTENER_SMCINVOKE_ID; __qseecom_reentrancy_check_if_no_app_blocked(smc_id); ret = scm_call2(smc_id, &desc); if (ret) { + qseecom.smcinvoke_support = false; smc_id = TZ_OS_REGISTER_LISTENER_ID; __qseecom_reentrancy_check_if_no_app_blocked( smc_id); @@ -1006,10 +1009,14 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, struct qseecom_continue_blocked_request_ireq *req = (struct qseecom_continue_blocked_request_ireq *) req_buf; - smc_id = TZ_OS_CONTINUE_BLOCKED_REQUEST_ID; + if (qseecom.smcinvoke_support) + smc_id = + TZ_OS_CONTINUE_BLOCKED_REQUEST_SMCINVOKE_ID; + else + smc_id = TZ_OS_CONTINUE_BLOCKED_REQUEST_ID; desc.arginfo = TZ_OS_CONTINUE_BLOCKED_REQUEST_ID_PARAM_ID; - desc.args[0] = req->app_id; + desc.args[0] = req->app_or_session_id; ret = scm_call2(smc_id, &desc); break; } @@ -1839,7 +1846,7 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data, return ret; } -int __qseecom_process_reentrancy_blocked_on_listener( +static int __qseecom_process_blocked_on_listener_legacy( struct qseecom_command_scm_resp *resp, struct qseecom_registered_app_list *ptr_app, struct qseecom_dev_handle *data) @@ -1848,9 +1855,8 @@ int __qseecom_process_reentrancy_blocked_on_listener( int ret = 0; struct qseecom_continue_blocked_request_ireq ireq; struct qseecom_command_scm_resp continue_resp; - sigset_t new_sigset, old_sigset; - unsigned long flags; bool found_app = false; + unsigned long flags; if (!resp || !data) { pr_err("invalid resp or data pointer\n"); @@ -1890,32 +1896,30 @@ int __qseecom_process_reentrancy_blocked_on_listener( pr_debug("lsntr %d in_use = %d\n", resp->data, list_ptr->listener_in_use); ptr_app->blocked_on_listener_id = resp->data; + /* sleep until listener is available */ - do { - qseecom.app_block_ref_cnt++; - ptr_app->app_blocked = true; - sigfillset(&new_sigset); - sigprocmask(SIG_SETMASK, &new_sigset, &old_sigset); - mutex_unlock(&app_access_lock); - do { - if (!wait_event_freezable( - list_ptr->listener_block_app_wq, - !list_ptr->listener_in_use)) { - break; - } - } while (1); - mutex_lock(&app_access_lock); - sigprocmask(SIG_SETMASK, &old_sigset, NULL); - ptr_app->app_blocked = false; - qseecom.app_block_ref_cnt--; - } while (list_ptr->listener_in_use == true); + qseecom.app_block_ref_cnt++; + ptr_app->app_blocked = true; + mutex_unlock(&app_access_lock); + if (wait_event_freezable( + list_ptr->listener_block_app_wq, + !list_ptr->listener_in_use)) { + pr_err("Interrupted: listener_id %d, app_id %d\n", + resp->data, ptr_app->app_id); + ret = -ERESTARTSYS; + goto exit; + } + mutex_lock(&app_access_lock); + ptr_app->app_blocked = false; + qseecom.app_block_ref_cnt--; + ptr_app->blocked_on_listener_id = 0; /* notify the blocked app that listener is available */ pr_warn("Lsntr %d is available, unblock app(%d) %s in TZ\n", resp->data, data->client.app_id, data->client.app_name); ireq.qsee_cmd_id = QSEOS_CONTINUE_BLOCKED_REQ_COMMAND; - ireq.app_id = data->client.app_id; + ireq.app_or_session_id = data->client.app_id; ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &ireq, sizeof(ireq), &continue_resp, sizeof(continue_resp)); @@ -1934,6 +1938,73 @@ exit: return ret; } +static int __qseecom_process_blocked_on_listener_smcinvoke( + struct qseecom_command_scm_resp *resp) +{ + struct qseecom_registered_listener_list *list_ptr; + int ret = 0; + struct qseecom_continue_blocked_request_ireq ireq; + struct qseecom_command_scm_resp continue_resp; + unsigned int session_id; + + if (!resp) { + pr_err("invalid resp pointer\n"); + ret = -EINVAL; + goto exit; + } + session_id = resp->resp_type; + list_ptr = __qseecom_find_svc(resp->data); + if (!list_ptr) { + pr_err("Invalid listener ID\n"); + ret = -ENODATA; + goto exit; + } + pr_debug("lsntr %d in_use = %d\n", + resp->data, list_ptr->listener_in_use); + /* sleep until listener is available */ + qseecom.app_block_ref_cnt++; + mutex_unlock(&app_access_lock); + if (wait_event_freezable( + list_ptr->listener_block_app_wq, + !list_ptr->listener_in_use)) { + pr_err("Interrupted: listener_id %d, session_id %d\n", + resp->data, session_id); + ret = -ERESTARTSYS; + goto exit; + } + mutex_lock(&app_access_lock); + qseecom.app_block_ref_cnt--; + + /* notify TZ that listener is available */ + pr_warn("Lsntr %d is available, unblock session(%d) in TZ\n", + resp->data, session_id); + ireq.qsee_cmd_id = QSEOS_CONTINUE_BLOCKED_REQ_COMMAND; + ireq.app_or_session_id = session_id; + ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, + &ireq, sizeof(ireq), + &continue_resp, sizeof(continue_resp)); + if (ret) { + pr_err("scm_call for continue blocked req for session %d failed, ret %d\n", + session_id, ret); + goto exit; + } + resp->result = QSEOS_RESULT_INCOMPLETE; +exit: + return ret; +} + +static int __qseecom_process_reentrancy_blocked_on_listener( + struct qseecom_command_scm_resp *resp, + struct qseecom_registered_app_list *ptr_app, + struct qseecom_dev_handle *data) +{ + if (!qseecom.smcinvoke_support) + return __qseecom_process_blocked_on_listener_legacy( + resp, ptr_app, data); + else + return __qseecom_process_blocked_on_listener_smcinvoke( + resp); +} static int __qseecom_reentrancy_process_incomplete_cmd( struct qseecom_dev_handle *data, struct qseecom_command_scm_resp *resp) @@ -4699,18 +4770,15 @@ int qseecom_process_listener_from_smcinvoke(struct scm_desc *desc) } resp.result = desc->ret[0]; /*req_cmd*/ - resp.resp_type = desc->ret[1]; /*app_id*/ + resp.resp_type = desc->ret[1]; /*incomplete:unused;blocked:session_id*/ resp.data = desc->ret[2]; /*listener_id*/ - dummy_private_data.client.app_id = desc->ret[1]; - dummy_app_entry.app_id = desc->ret[1]; - mutex_lock(&app_access_lock); ret = __qseecom_process_reentrancy(&resp, &dummy_app_entry, &dummy_private_data); mutex_unlock(&app_access_lock); if (ret) - pr_err("Failed to req cmd %d lsnr %d on app %d, ret = %d\n", + pr_err("Failed on cmd %d for lsnr %d session %d, ret = %d\n", (int)desc->ret[0], (int)desc->ret[2], (int)desc->ret[1], ret); desc->ret[0] = resp.result; @@ -8366,9 +8434,10 @@ out: */ static int qseecom_check_whitelist_feature(void) { - int version = scm_get_feat_version(FEATURE_ID_WHITELIST); + u64 version = 0; + int ret = scm_get_feat_version(FEATURE_ID_WHITELIST, &version); - return version >= MAKE_WHITELIST_VERSION(1, 0, 0); + return (ret == 0) && (version >= MAKE_WHITELIST_VERSION(1, 0, 0)); } static int qseecom_probe(struct platform_device *pdev) @@ -8462,7 +8531,11 @@ static int qseecom_probe(struct platform_device *pdev) qseecom.ion_clnt = msm_ion_client_create("qseecom-kernel"); if (IS_ERR_OR_NULL(qseecom.ion_clnt)) { pr_err("Ion client cannot be created\n"); - rc = -ENOMEM; + + if (qseecom.ion_clnt != ERR_PTR(-EPROBE_DEFER)) + rc = -ENOMEM; + else + rc = -EPROBE_DEFER; goto exit_del_cdev; } diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 01e5502917f7..d39b4056c169 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1173,7 +1173,7 @@ idata_free: cmd_done: mmc_blk_put(md); - if (card->cmdq_init) + if (card && card->cmdq_init) wake_up(&card->host->cmdq_ctx.wait); return err; } @@ -4623,6 +4623,10 @@ static int mmc_blk_probe(struct mmc_card *card) dev_set_drvdata(&card->dev, md); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + mmc_set_bus_resume_policy(card->host, 1); +#endif + if (mmc_add_disk(md)) goto out; @@ -4666,6 +4670,9 @@ static void mmc_blk_remove(struct mmc_card *card) pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); dev_set_drvdata(&card->dev, NULL); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + mmc_set_bus_resume_policy(card->host, 0); +#endif } static int _mmc_blk_suspend(struct mmc_card *card, bool wait) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 8dc93900b16d..ab4837128cb2 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -4179,8 +4179,10 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host, /* keep track of the value in SDHCI_CAPABILITIES */ msm_host->caps_0 = caps; - if ((major == 1) && (minor >= 0x6b)) + if ((major == 1) && (minor >= 0x6b)) { msm_host->ice_hci_support = true; + host->cdr_support = true; + } } #ifdef CONFIG_MMC_CQ_HCI @@ -4247,6 +4249,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) struct resource *tlmm_memres = NULL; void __iomem *tlmm_mem; unsigned long flags; + bool force_probe; pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__); msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host), @@ -4310,8 +4313,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto pltfm_free; } + /* Read property to determine if the probe is forced */ + force_probe = of_find_property(pdev->dev.of_node, + "qcom,force-sdhc1-probe", NULL); + /* skip the probe if eMMC isn't a boot device */ - if ((ret == 1) && !sdhci_msm_is_bootdevice(&pdev->dev)) { + if ((ret == 1) && !sdhci_msm_is_bootdevice(&pdev->dev) + && !force_probe) { ret = -ENODEV; goto pltfm_free; } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a5ff9f73dfbc..0033fea0a800 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3633,7 +3633,7 @@ static void sdhci_cmdq_set_transfer_params(struct mmc_host *mmc) ctrl |= SDHCI_CTRL_ADMA32; sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } - if (host->ops->toggle_cdr) + if (host->ops->toggle_cdr && !host->cdr_support) host->ops->toggle_cdr(host, false); } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 93129b26dc5e..300be7fd0f24 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -564,6 +564,7 @@ struct sdhci_host { bool runtime_suspended; /* Host is runtime suspended */ bool bus_on; /* Bus power prevents runtime suspend */ bool preset_enabled; /* Preset is enabled */ + bool cdr_support; struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ diff --git a/drivers/net/can/spi/rh850.c b/drivers/net/can/spi/rh850.c index a93f979da9ed..d2b6e8caa112 100644 --- a/drivers/net/can/spi/rh850.c +++ b/drivers/net/can/spi/rh850.c @@ -1020,6 +1020,8 @@ static int rh850_create_netdev(struct spi_device *spi, return -ENOMEM; } + netdev->mtu = CANFD_MTU; + netdev_priv_data = netdev_priv(netdev); netdev_priv_data->rh850_can = priv_data; netdev_priv_data->netdev_index = index; diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 70da30095b89..a5e4b4b93d1b 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1583,6 +1583,11 @@ static int bgmac_probe(struct bcma_device *core) dev_warn(&core->dev, "Using random MAC: %pM\n", mac); } + /* This (reset &) enable is not preset in specs or reference driver but + * Broadcom does it in arch PCI code when enabling fake PCI device. + */ + bcma_core_enable(core, 0); + /* Allocation and references */ net_dev = alloc_etherdev(sizeof(*bgmac)); if (!net_dev) diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c index 2a9dd460a95f..e1f9e7cebf8f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.c +++ b/drivers/net/ethernet/mellanox/mlx4/icm.c @@ -118,8 +118,13 @@ static int mlx4_alloc_icm_coherent(struct device *dev, struct scatterlist *mem, if (!buf) return -ENOMEM; + if (offset_in_page(buf)) { + dma_free_coherent(dev, PAGE_SIZE << order, + buf, sg_dma_address(mem)); + return -ENOMEM; + } + sg_set_buf(mem, buf, PAGE_SIZE << order); - BUG_ON(mem->offset); sg_dma_len(mem) = PAGE_SIZE << order; return 0; } diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c index 60b7a64c2edb..de14dcc6f4ed 100644 --- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c +++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c @@ -942,10 +942,13 @@ net_dev_reg_fail: netif_napi_del(&(rmnet_mhi_ptr->napi)); free_netdev(rmnet_mhi_ptr->dev); net_dev_alloc_fail: - mhi_close_channel(rmnet_mhi_ptr->rx_client_handle); - rmnet_mhi_ptr->dev = NULL; + if (rmnet_mhi_ptr->rx_client_handle) { + mhi_close_channel(rmnet_mhi_ptr->rx_client_handle); + rmnet_mhi_ptr->dev = NULL; + } mhi_rx_chan_start_fail: - mhi_close_channel(rmnet_mhi_ptr->tx_client_handle); + if (rmnet_mhi_ptr->tx_client_handle) + mhi_close_channel(rmnet_mhi_ptr->tx_client_handle); mhi_tx_chan_start_fail: rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited ret %d.\n", ret); return ret; diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 79ef799f88ab..c5ea1018cb47 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -326,6 +326,7 @@ enum cfg_version { static const struct pci_device_id rtl8169_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8129), 0, 0, RTL_CFG_0 }, { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8136), 0, 0, RTL_CFG_2 }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8161), 0, 0, RTL_CFG_1 }, { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), 0, 0, RTL_CFG_0 }, { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), 0, 0, RTL_CFG_1 }, { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), 0, 0, RTL_CFG_0 }, diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 480f3dae0780..4296066a7ad3 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -750,6 +750,7 @@ static struct sh_eth_cpu_data sh7734_data = { .tsu = 1, .hw_crc = 1, .select_mii = 1, + .shift_rd0 = 1, }; /* SH7763 */ diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 84b9cca152eb..e83acc608678 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -907,7 +907,7 @@ static void decode_txts(struct dp83640_private *dp83640, if (overflow) { pr_debug("tx timestamp queue overflow, count %d\n", overflow); while (skb) { - skb_complete_tx_timestamp(skb, NULL); + kfree_skb(skb); skb = skb_dequeue(&dp83640->tx_queue); } return; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index e13ad6cdcc22..c8b85f1069ff 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -539,6 +539,8 @@ static int ksz9031_read_status(struct phy_device *phydev) if ((regval & 0xFF) == 0xFF) { phy_init_hw(phydev); phydev->link = 0; + if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev)) + phydev->drv->config_intr(phydev); } return 0; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0bfbabad4431..1d1e5f7723ab 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1442,7 +1442,7 @@ static struct phy_driver genphy_driver[] = { .phy_id = 0xffffffff, .phy_id_mask = 0xffffffff, .name = "Generic PHY", - .soft_reset = genphy_soft_reset, + .soft_reset = genphy_no_soft_reset, .config_init = genphy_config_init, .features = PHY_GBIT_FEATURES | SUPPORTED_MII | SUPPORTED_AUI | SUPPORTED_FIBRE | diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index f64b25c221e8..cd93220c9b45 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -1009,6 +1009,7 @@ static int kaweth_probe( struct net_device *netdev; const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; int result = 0; + int rv = -EIO; dev_dbg(dev, "Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x\n", @@ -1029,6 +1030,7 @@ static int kaweth_probe( kaweth = netdev_priv(netdev); kaweth->dev = udev; kaweth->net = netdev; + kaweth->intf = intf; spin_lock_init(&kaweth->device_lock); init_waitqueue_head(&kaweth->term_wait); @@ -1048,6 +1050,10 @@ static int kaweth_probe( /* Download the firmware */ dev_info(dev, "Downloading firmware...\n"); kaweth->firmware_buf = (__u8 *)__get_free_page(GFP_KERNEL); + if (!kaweth->firmware_buf) { + rv = -ENOMEM; + goto err_free_netdev; + } if ((result = kaweth_download_firmware(kaweth, "kaweth/new_code.bin", 100, @@ -1139,8 +1145,6 @@ err_fw: dev_dbg(dev, "Initializing net device.\n"); - kaweth->intf = intf; - kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!kaweth->tx_urb) goto err_free_netdev; @@ -1204,7 +1208,7 @@ err_only_tx: err_free_netdev: free_netdev(netdev); - return -EIO; + return rv; } /**************************************************************** diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 349aecbc210a..ac945f8781ac 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -733,15 +733,15 @@ static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev) static void vrf_dev_uninit(struct net_device *dev) { struct net_vrf *vrf = netdev_priv(dev); - struct slave_queue *queue = &vrf->queue; - struct list_head *head = &queue->all_slaves; - struct slave *slave, *next; +// struct slave_queue *queue = &vrf->queue; +// struct list_head *head = &queue->all_slaves; +// struct slave *slave, *next; vrf_rtable_destroy(vrf); vrf_rt6_destroy(vrf); - list_for_each_entry_safe(slave, next, head, list) - vrf_del_slave(dev, slave->dev); +// list_for_each_entry_safe(slave, next, head, list) +// vrf_del_slave(dev, slave->dev); free_percpu(dev->dstats); dev->dstats = NULL; @@ -914,6 +914,14 @@ static int vrf_validate(struct nlattr *tb[], struct nlattr *data[]) static void vrf_dellink(struct net_device *dev, struct list_head *head) { + struct net_vrf *vrf = netdev_priv(dev); + struct slave_queue *queue = &vrf->queue; + struct list_head *all_slaves = &queue->all_slaves; + struct slave *slave, *next; + + list_for_each_entry_safe(slave, next, all_slaves, list) + vrf_del_slave(dev, slave->dev); + unregister_netdevice_queue(dev, head); } diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 6906bddb229f..041d1d5eb718 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1886,6 +1886,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, goto err_wmi_detach; } + /* If firmware indicates Full Rx Reorder support it must be used in a + * slightly different manner. Let HTT code know. + */ + ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER, + ar->wmi.svc_map)); + status = ath10k_htt_rx_alloc(&ar->htt); if (status) { ath10k_err(ar, "failed to alloc htt rx: %d\n", status); @@ -1918,6 +1924,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, goto err_hif_stop; } + status = ath10k_pktlog_connect(ar); + if (status) { + ath10k_err(ar, "could not connect pktlog: %d\n", status); + goto err_hif_stop; + } + status = ath10k_htc_start(&ar->htc); if (status) { ath10k_err(ar, "failed to start htc: %d\n", status); @@ -1997,12 +2009,6 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, } } - /* If firmware indicates Full Rx Reorder support it must be used in a - * slightly different manner. Let HTT code know. - */ - ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER, - ar->wmi.svc_map)); - status = ath10k_htt_rx_ring_refill(ar); if (status) { ath10k_err(ar, "failed to refill htt rx ring: %d\n", status); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 8ddbae96794d..ffcf30756b9e 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -467,6 +467,7 @@ struct ath10k_debug { u64 fw_dbglog_mask; u32 fw_dbglog_level; u32 pktlog_filter; + enum ath10k_htc_ep_id eid; u32 reg_addr; u32 nf_cal_period; void *cal_data; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index ec8063e7986a..42aab9b86af3 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -25,6 +25,7 @@ #include "core.h" #include "debug.h" #include "hif.h" +#include "htt.h" #include "wmi-ops.h" /* ms */ @@ -2617,6 +2618,178 @@ static const struct file_operations fops_fw_checksums = { .llseek = default_llseek, }; +static struct txctl_frm_hdr frm_hdr; + +static void ath10k_extract_frame_header(u8 *addr1, u8 *addr2, u8 *addr3) +{ + frm_hdr.bssid_tail = (addr1[IEEE80211_ADDR_LEN - 2] << BITS_PER_BYTE) + | (addr1[IEEE80211_ADDR_LEN - 1]); + frm_hdr.sa_tail = (addr2[IEEE80211_ADDR_LEN - 2] << BITS_PER_BYTE) + | (addr2[IEEE80211_ADDR_LEN - 1]); + frm_hdr.da_tail = (addr3[IEEE80211_ADDR_LEN - 2] << BITS_PER_BYTE) + | (addr3[IEEE80211_ADDR_LEN - 1]); +} + +static void ath10k_process_ieee_hdr(void *data) +{ + u8 dir; + struct ieee80211_frame *wh; + + if (!data) + return; + + wh = (struct ieee80211_frame *)(data); + frm_hdr.framectrl = *(u_int16_t *)(wh->i_fc); + frm_hdr.seqctrl = *(u_int16_t *)(wh->i_seq); + dir = (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK); + + if (dir == IEEE80211_FC1_DIR_TODS) + ath10k_extract_frame_header(wh->i_addr1, wh->i_addr2, + wh->i_addr3); + else if (dir == IEEE80211_FC1_DIR_FROMDS) + ath10k_extract_frame_header(wh->i_addr2, wh->i_addr3, + wh->i_addr1); + else + ath10k_extract_frame_header(wh->i_addr3, wh->i_addr2, + wh->i_addr1); +} + +static void ath10k_pktlog_process_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct ath10k_pktlog_hdr *hdr = (void *)skb->data; + struct ath_pktlog_txctl pktlog_tx_ctrl; + + switch (hdr->log_type) { + case ATH10K_PKTLOG_TYPE_TX_CTRL: { + spin_lock_bh(&ar->htt.tx_lock); + + memcpy((void *)(&pktlog_tx_ctrl.hdr), (void *)hdr, + sizeof(pktlog_tx_ctrl.hdr)); + pktlog_tx_ctrl.frm_hdr = frm_hdr; + memcpy((void *)pktlog_tx_ctrl.txdesc_ctl, (void *)hdr->payload, + __le16_to_cpu(hdr->size)); + pktlog_tx_ctrl.hdr.size = sizeof(pktlog_tx_ctrl) - + sizeof(pktlog_tx_ctrl.hdr); + + spin_unlock_bh(&ar->htt.tx_lock); + + trace_ath10k_htt_pktlog(ar, (void *)&pktlog_tx_ctrl, + sizeof(pktlog_tx_ctrl)); + break; + } + case ATH10K_PKTLOG_TYPE_TX_MSDU_ID: + break; + case ATH10K_PKTLOG_TYPE_TX_FRM_HDR: { + ath10k_process_ieee_hdr((void *)(hdr->payload)); + trace_ath10k_htt_pktlog(ar, hdr, sizeof(*hdr) + + __le16_to_cpu(hdr->size)); + break; + } + case ATH10K_PKTLOG_TYPE_RX_STAT: + case ATH10K_PKTLOG_TYPE_RC_FIND: + case ATH10K_PKTLOG_TYPE_RC_UPDATE: + case ATH10K_PKTLOG_TYPE_DBG_PRINT: + case ATH10K_PKTLOG_TYPE_TX_STAT: + case ATH10K_PKTLOG_TYPE_SW_EVENT: + trace_ath10k_htt_pktlog(ar, hdr, sizeof(*hdr) + + __le16_to_cpu(hdr->size)); + break; + case ATH10K_PKTLOG_TYPE_TX_VIRT_ADDR: { + u32 desc_id = (u32)*((u32 *)(hdr->payload)); + struct sk_buff *msdu; + + spin_lock_bh(&ar->htt.tx_lock); + msdu = ath10k_htt_tx_find_msdu_by_id(&ar->htt, desc_id); + + if (!msdu) { + ath10k_info(ar, + "Failed to get msdu, id: %d\n", + desc_id); + spin_unlock_bh(&ar->htt.tx_lock); + return; + } + ath10k_process_ieee_hdr((void *)msdu->data); + spin_unlock_bh(&ar->htt.tx_lock); + trace_ath10k_htt_pktlog(ar, hdr, sizeof(*hdr) + + __le16_to_cpu(hdr->size)); + break; + } + } +} + +int ath10k_rx_record_pktlog(struct ath10k *ar, struct sk_buff *skb) +{ + struct sk_buff *pktlog_skb; + struct ath_pktlog_hdr *pl_hdr; + struct ath_pktlog_rx_info *pktlog_rx_info; + struct htt_rx_desc *rx_desc = (void *)skb->data - sizeof(*rx_desc); + + if (!ar->debug.pktlog_filter) + return 0; + + pktlog_skb = dev_alloc_skb(sizeof(struct ath_pktlog_hdr) + + sizeof(struct htt_rx_desc) - + sizeof(struct htt_host_fw_desc_base)); + if (!pktlog_skb) + return -ENOMEM; + + pktlog_rx_info = (struct ath_pktlog_rx_info *)pktlog_skb->data; + pl_hdr = &pktlog_rx_info->pl_hdr; + + pl_hdr->flags = (1 << ATH10K_PKTLOG_FLG_FRM_TYPE_REMOTE_S); + pl_hdr->missed_cnt = 0; + pl_hdr->mac_id = 0; + pl_hdr->log_type = ATH10K_PKTLOG_TYPE_RX_STAT; + pl_hdr->flags |= ATH10K_PKTLOG_HDR_SIZE_16; + pl_hdr->size = sizeof(*rx_desc) - + sizeof(struct htt_host_fw_desc_base); + + pl_hdr->timestamp = + cpu_to_le32(rx_desc->ppdu_end.wcn3990.rx_pkt_end.phy_timestamp_1); + + pl_hdr->type_specific_data = 0xDEADAA; + memcpy((void *)pktlog_rx_info + sizeof(struct ath_pktlog_hdr), + (void *)rx_desc + sizeof(struct htt_host_fw_desc_base), + pl_hdr->size); + + ath10k_pktlog_process_rx(ar, pktlog_skb); + dev_kfree_skb_any(pktlog_skb); + return 0; +} + +static void ath10k_pktlog_htc_tx_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_info(ar, "PKTLOG htc completed\n"); +} + +int ath10k_pktlog_connect(struct ath10k *ar) +{ + int status; + struct ath10k_htc_svc_conn_req conn_req; + struct ath10k_htc_svc_conn_resp conn_resp; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + conn_req.ep_ops.ep_tx_complete = ath10k_pktlog_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath10k_pktlog_process_rx; + conn_req.ep_ops.ep_tx_credits = NULL; + + /* connect to control service */ + conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_LOG_MSG; + status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp); + if (status) { + ath10k_warn(ar, "failed to connect to PKTLOG service: %d\n", + status); + return status; + } + + ar->debug.eid = conn_resp.eid; + + return 0; +} + int ath10k_debug_create(struct ath10k *ar) { ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data)); diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index f963391e3544..eb47720bbf91 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -57,6 +57,84 @@ enum ath10k_dbg_aggr_mode { ATH10K_DBG_AGGR_MODE_MAX, }; +#define IEEE80211_FC1_DIR_MASK 0x03 +#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */ +#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */ +#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */ +#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */ +#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */ + +#define MAX_PKT_INFO_MSDU_ID 192 +#define MSDU_ID_INFO_ID_OFFSET \ + ((MAX_PKT_INFO_MSDU_ID >> 3) + 4) + +#define PKTLOG_MAX_TXCTL_WORDS 57 /* +2 words for bitmap */ +#define HTT_TX_MSDU_LEN_MASK 0xffff + +struct txctl_frm_hdr { + __le16 framectrl; /* frame control field from header */ + __le16 seqctrl; /* frame control field from header */ + __le16 bssid_tail; /* last two octets of bssid */ + __le16 sa_tail; /* last two octets of SA */ + __le16 da_tail; /* last two octets of DA */ + __le16 resvd; +} __packed; + +struct ath_pktlog_hdr { + __le16 flags; + __le16 missed_cnt; + u8 log_type; + u8 mac_id; + __le16 size; + __le32 timestamp; + __le32 type_specific_data; +} __packed; + +/* generic definitions for IEEE 802.11 frames */ +struct ieee80211_frame { + u8 i_fc[2]; + u8 i_dur[2]; + union { + struct { + u8 i_addr1[IEEE80211_ADDR_LEN]; + u8 i_addr2[IEEE80211_ADDR_LEN]; + u8 i_addr3[IEEE80211_ADDR_LEN]; + }; + u8 i_addr_all[3 * IEEE80211_ADDR_LEN]; + }; + u8 i_seq[2]; +} __packed; + +struct fw_pktlog_msdu_info { + __le32 num_msdu; + u8 bound_bmap[MAX_PKT_INFO_MSDU_ID >> 3]; + __le16 id[MAX_PKT_INFO_MSDU_ID]; +} __packed; + +struct ath_pktlog_txctl { + struct ath_pktlog_hdr hdr; + struct txctl_frm_hdr frm_hdr; + __le32 txdesc_ctl[PKTLOG_MAX_TXCTL_WORDS]; +} __packed; + +struct ath_pktlog_msdu_id { + struct ath_pktlog_hdr hdr; + struct fw_pktlog_msdu_info msdu_info; +} __packed; + +struct ath_pktlog_rx_info { + struct ath_pktlog_hdr pl_hdr; + struct rx_attention attention; + struct rx_frag_info frag_info; + struct rx_mpdu_start mpdu_start; + struct rx_msdu_start msdu_start; + struct rx_msdu_end msdu_end; + struct rx_mpdu_end mpdu_end; + struct rx_ppdu_start ppdu_start; + struct rx_ppdu_end ppdu_end; + u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN]; +} __packed; + /* FIXME: How to calculate the buffer size sanely? */ #define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024) #define ATH10K_DATAPATH_BUF_SIZE (1024 * 1024) @@ -86,6 +164,7 @@ struct ath10k_fw_crash_data * ath10k_debug_get_new_fw_crash_data(struct ath10k *ar); void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len); +int ath10k_rx_record_pktlog(struct ath10k *ar, struct sk_buff *skb); #define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++) void ath10k_debug_get_et_strings(struct ieee80211_hw *hw, @@ -98,6 +177,7 @@ void ath10k_debug_get_et_stats(struct ieee80211_hw *hw, struct ethtool_stats *stats, u64 *data); void fill_datapath_stats(struct ath10k *ar, struct ieee80211_rx_status *status); size_t get_datapath_stat(char *buf, struct ath10k *ar); +int ath10k_pktlog_connect(struct ath10k *ar); #else static inline int ath10k_debug_start(struct ath10k *ar) { @@ -142,6 +222,12 @@ static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, { } +static inline int ath10k_rx_record_pktlog(struct ath10k *ar, + struct sk_buff *skb) +{ + return 0; +} + static inline struct ath10k_fw_crash_data * ath10k_debug_get_new_fw_crash_data(struct ath10k *ar) { @@ -158,6 +244,10 @@ static inline size_t get_datapath_stat(char *buf, struct ath10k *ar) return 0; } +static inline int ath10k_pktlog_connect(struct ath10k *ar) +{ + return 0; +} #define ATH10K_DFS_STAT_INC(ar, c) do { } while (0) #define ath10k_debug_get_et_strings NULL diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index b0d6c2614731..6377c4ef427c 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1745,16 +1745,21 @@ struct ath10k_htt { #define RX_HTT_HDR_STATUS_LEN 64 -/* This structure layout is programmed via rx ring setup - * so that FW knows how to transfer the rx descriptor to the host. - * Buffers like this are placed on the rx ring. */ -struct htt_rx_desc { +struct htt_host_fw_desc_base { union { /* This field is filled on the host using the msdu buffer * from htt_rx_indication */ struct fw_rx_desc_base fw_desc; u32 pad; } __packed; +}; + +/* This structure layout is programmed via rx ring setup + * so that FW knows how to transfer the rx descriptor to the host. + * Buffers like this are placed on the rx ring. + */ +struct htt_rx_desc { + struct htt_host_fw_desc_base fw_desc_base; struct { struct rx_attention attention; struct rx_frag_info frag_info; @@ -1847,5 +1852,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget); +struct sk_buff *ath10k_htt_tx_find_msdu_by_id(struct ath10k_htt *htt, + u16 msdu_id); #endif diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 437ea2c192b3..635b8281b055 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -969,6 +969,7 @@ static void ath10k_process_rx(struct ath10k *ar, trace_ath10k_rx_hdr(ar, skb->data, skb->len); trace_ath10k_rx_payload(ar, skb->data, skb->len); + ath10k_rx_record_pktlog(ar, skb); ieee80211_rx_napi(ar->hw, NULL, skb, &ar->napi); } diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 1b59721a91ac..6579eb2b410c 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -218,6 +218,27 @@ int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb) return ret; } +struct sk_buff *ath10k_htt_tx_find_msdu_by_id(struct ath10k_htt *htt, + u16 msdu_id) +{ + struct ath10k *ar; + struct sk_buff *ret; + + if (!htt) + return NULL; + + ar = htt->ar; + + lockdep_assert_held(&htt->tx_lock); + + ret = (struct sk_buff *)idr_find(&htt->pending_tx, msdu_id); + + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx find msdu by msdu_id %s\n", + !ret ? "Failed" : "Success"); + + return ret; +} + void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) { struct ath10k *ar = htt->ar; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 0f2422480c4e..37479589b8e1 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -984,4 +984,37 @@ struct ath10k_shadow_reg_address { extern struct ath10k_shadow_reg_value wcn3990_shadow_reg_value; extern struct ath10k_shadow_reg_address wcn3990_shadow_reg_address; +#define ATH10K_PKTLOG_HDR_SIZE_16 0x8000 + +enum { + ATH10k_PKTLOG_FLG_FRM_TYPE_LOCAL_S = 0, + ATH10K_PKTLOG_FLG_FRM_TYPE_REMOTE_S, + ATH10K_PKTLOG_FLG_FRM_TYPE_CLONE_S, + ATH10K_PKTLOG_FLG_FRM_TYPE_CBF_S, + ATH10K_PKTLOG_FLG_FRM_TYPE_UNKNOWN_S +}; + +enum ath10k_pktlog_type { + ATH10K_PKTLOG_TYPE_TX_CTRL = 1, + ATH10K_PKTLOG_TYPE_TX_STAT, + ATH10K_PKTLOG_TYPE_TX_MSDU_ID, + ATH10K_PKTLOG_TYPE_TX_FRM_HDR, + ATH10K_PKTLOG_TYPE_RX_STAT, + ATH10K_PKTLOG_TYPE_RC_FIND, + ATH10K_PKTLOG_TYPE_RC_UPDATE, + ATH10K_PKTLOG_TYPE_TX_VIRT_ADDR, + ATH10K_PKTLOG_TYPE_DBG_PRINT, + ATH10K_PKTLOG_TYPE_SW_EVENT, + ATH10K_PKTLOG_TYPE_MAX, +}; + +struct ath10k_pktlog_hdr { + __le16 flags; + __le16 missed_cnt; + __le16 log_type; + __le16 size; + __le32 timestamp; + u8 payload[0]; +} __packed; + #endif /* _HW_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index e430bab869a8..ec86c837e60a 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -667,6 +667,36 @@ ath10k_mac_get_any_chandef_iter(struct ieee80211_hw *hw, *def = &conf->def; } +static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) +{ + int ret; + unsigned long time_left; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath10k_wmi_peer_delete(ar, vdev_id, addr); + if (ret) + return ret; + + ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr); + if (ret) + return ret; + + if (QCA_REV_WCN3990(ar)) { + time_left = wait_for_completion_timeout(&ar->peer_delete_done, + 50 * HZ); + + if (time_left == 0) { + ath10k_warn(ar, "Timeout in receiving peer delete response\n"); + return -ETIMEDOUT; + } + } + + ar->num_peers--; + + return 0; +} + static int ath10k_peer_create(struct ath10k *ar, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -711,7 +741,7 @@ static int ath10k_peer_create(struct ath10k *ar, spin_unlock_bh(&ar->data_lock); ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n", addr, vdev_id); - ath10k_wmi_peer_delete(ar, vdev_id, addr); + ath10k_peer_delete(ar, vdev_id, addr); return -ENOENT; } @@ -779,36 +809,6 @@ static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value) return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value); } -static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) -{ - int ret; - unsigned long time_left; - - lockdep_assert_held(&ar->conf_mutex); - - ret = ath10k_wmi_peer_delete(ar, vdev_id, addr); - if (ret) - return ret; - - ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr); - if (ret) - return ret; - - if (QCA_REV_WCN3990(ar)) { - time_left = wait_for_completion_timeout(&ar->peer_delete_done, - 50 * HZ); - - if (time_left == 0) { - ath10k_warn(ar, "Timeout in receiving peer delete response\n"); - return -ETIMEDOUT; - } - } - - ar->num_peers--; - - return 0; -} - static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) { struct ath10k_peer *peer, *tmp; @@ -5094,7 +5094,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, err_peer_delete: if (arvif->vdev_type == WMI_VDEV_TYPE_AP || arvif->vdev_type == WMI_VDEV_TYPE_IBSS) - ath10k_wmi_peer_delete(ar, arvif->vdev_id, vif->addr); + ath10k_peer_delete(ar, arvif->vdev_id, vif->addr); err_vdev_delete: ath10k_wmi_vdev_delete(ar, arvif->vdev_id); @@ -5150,8 +5150,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, if (arvif->vdev_type == WMI_VDEV_TYPE_AP || arvif->vdev_type == WMI_VDEV_TYPE_IBSS) { - ret = ath10k_wmi_peer_delete(arvif->ar, arvif->vdev_id, - vif->addr); + ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, + vif->addr); if (ret) ath10k_warn(ar, "failed to submit AP/IBSS self-peer removal on vdev %i: %d\n", arvif->vdev_id, ret); @@ -7168,12 +7168,25 @@ ath10k_mac_update_vif_chan(struct ath10k *ar, if (WARN_ON(!arvif->is_up)) continue; + if (QCA_REV_WCN3990(ar)) { + /* In the case of wcn3990 WLAN module we send + * vdev restart only, no need to send vdev down. + */ - ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); - if (ret) { - ath10k_warn(ar, "failed to down vdev %d: %d\n", - arvif->vdev_id, ret); - continue; + ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def); + if (ret) { + ath10k_warn(ar, + "failed to restart vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } else { + ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) { + ath10k_warn(ar, "failed to down vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } } } @@ -7204,11 +7217,17 @@ ath10k_mac_update_vif_chan(struct ath10k *ar, ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n", ret); - ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def); - if (ret) { - ath10k_warn(ar, "failed to restart vdev %d: %d\n", - arvif->vdev_id, ret); - continue; + if (!QCA_REV_WCN3990(ar)) { + /* In case of other than wcn3990 WLAN module we + * send vdev down and vdev restart to the firmware. + */ + + ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def); + if (ret) { + ath10k_warn(ar, "failed to restart vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } } ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, @@ -7803,10 +7822,6 @@ static const struct ieee80211_iface_limit ath10k_wcn3990_if_limit[] = { BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_DEVICE), - }, }; static const struct ieee80211_iface_limit ath10k_wcn3990_qcs_if_limit[] = { @@ -7826,10 +7841,6 @@ static const struct ieee80211_iface_limit ath10k_wcn3990_qcs_if_limit[] = { #endif BIT(NL80211_IFTYPE_P2P_GO), }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_DEVICE), - }, }; static const struct ieee80211_iface_limit ath10k_wcn3990_if_limit_ibss[] = { @@ -8003,6 +8014,10 @@ int ath10k_mac_register(struct ath10k *ar) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); + if (QCA_REV_WCN3990(ar)) + ar->hw->wiphy->interface_modes &= + ~BIT(NL80211_IFTYPE_P2P_DEVICE); + ieee80211_hw_set(ar->hw, SIGNAL_DBM); ieee80211_hw_set(ar->hw, SUPPORTS_PS); ieee80211_hw_set(ar->hw, SUPPORTS_DYNAMIC_PS); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 9e607b2fa2d4..ca77861c4320 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3139,7 +3139,7 @@ int ath10k_pci_setup_resource(struct ath10k *ar) setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry, (unsigned long)ar); - if (QCA_REV_6174(ar)) + if (QCA_REV_6174(ar) || QCA_REV_9377(ar)) ath10k_pci_override_ce_config(ar); ret = ath10k_pci_alloc_pipes(ar); diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 9406b6f71a6b..13736750e463 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -107,8 +107,8 @@ static struct ce_attr host_ce_config_wlan[] = { { .flags = CE_ATTR_FLAGS, .src_nentries = 0, - .src_sz_max = 512, - .dest_nentries = 512, + .src_sz_max = 0, + .dest_nentries = 0, .recv_cb = ath10k_snoc_htt_rx_cb, }, @@ -775,9 +775,6 @@ static int ath10k_snoc_hif_map_service_to_pipe(struct ath10k *ar, } } - if (WARN_ON(!ul_set || !dl_set)) - return -ENOENT; - return 0; } @@ -1274,19 +1271,19 @@ static int ath10k_snoc_probe(struct platform_device *pdev) ret = ath10k_snoc_claim(ar); if (ret) { ath10k_err(ar, "failed to claim device: %d\n", ret); - goto err_core_destroy; + goto err_stop_qmi_service; } ret = ath10k_snoc_bus_configure(ar); if (ret) { ath10k_err(ar, "failed to configure bus: %d\n", ret); - goto err_core_destroy; + goto err_stop_qmi_service; } ret = ath10k_snoc_alloc_pipes(ar); if (ret) { ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", ret); - goto err_core_destroy; + goto err_stop_qmi_service; } netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll, @@ -1319,6 +1316,9 @@ err_free_irq: err_free_pipes: ath10k_snoc_free_pipes(ar); +err_stop_qmi_service: + ath10k_snoc_stop_qmi_service(ar); + err_core_destroy: ath10k_core_destroy(ar); diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index 129859255295..8bb01c1a35f7 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -31,6 +31,8 @@ struct wmi_ops { struct wmi_mgmt_rx_ev_arg *arg); int (*pull_ch_info)(struct ath10k *ar, struct sk_buff *skb, struct wmi_ch_info_ev_arg *arg); + int (*pull_peer_delete_resp)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_peer_delete_resp_ev_arg *arg); int (*pull_vdev_start)(struct ath10k *ar, struct sk_buff *skb, struct wmi_vdev_start_ev_arg *arg); int (*pull_peer_kick)(struct ath10k *ar, struct sk_buff *skb, @@ -246,6 +248,16 @@ ath10k_wmi_pull_mgmt_rx(struct ath10k *ar, struct sk_buff *skb, } static inline int +ath10k_wmi_pull_peer_delete_resp(struct ath10k *ar, struct sk_buff *skb, + struct wmi_peer_delete_resp_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_peer_delete_resp) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_peer_delete_resp(ar, skb, arg); +} + +static inline int ath10k_wmi_pull_ch_info(struct ath10k *ar, struct sk_buff *skb, struct wmi_ch_info_ev_arg *arg) { @@ -669,6 +681,9 @@ ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, struct sk_buff *skb; u32 cmd_id; + if (!ar->wmi.ops->gen_vdev_spectral_conf) + return -EOPNOTSUPP; + skb = ar->wmi.ops->gen_vdev_spectral_conf(ar, arg); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -684,6 +699,9 @@ ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, struct sk_buff *skb; u32 cmd_id; + if (!ar->wmi.ops->gen_vdev_spectral_enable) + return -EOPNOTSUPP; + skb = ar->wmi.ops->gen_vdev_spectral_enable(ar, vdev_id, trigger, enable); if (IS_ERR(skb)) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 888ab5ad37f1..07b15f4c1db4 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -412,15 +412,6 @@ static int ath10k_wmi_tlv_event_tx_pause(struct ath10k *ar, return 0; } -static int ath10k_wmi_tlv_event_peer_delete_resp(struct ath10k *ar, - struct sk_buff *skb) -{ - ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TLV_PEER_DELETE_RESP_EVENTID\n"); - complete(&ar->peer_delete_done); - - return 0; -} - /***********/ /* TLV ops */ /***********/ @@ -657,6 +648,34 @@ static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, return 0; } +static int ath10k_wmi_tlv_op_pull_peer_delete_ev( + struct ath10k *ar, struct sk_buff *skb, + struct wmi_peer_delete_resp_ev_arg *arg) +{ + const void **tb; + const struct wmi_peer_delete_resp_ev_arg *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_PEER_DELETE_RESP_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + arg->vdev_id = ev->vdev_id; + arg->peer_addr = ev->peer_addr; + + kfree(tb); + return 0; +} + static int ath10k_wmi_tlv_op_pull_ch_info_ev(struct ath10k *ar, struct sk_buff *skb, struct wmi_ch_info_ev_arg *arg) @@ -3620,6 +3639,7 @@ static const struct wmi_ops wmi_tlv_ops = { .pull_scan = ath10k_wmi_tlv_op_pull_scan_ev, .pull_mgmt_rx = ath10k_wmi_tlv_op_pull_mgmt_rx_ev, .pull_ch_info = ath10k_wmi_tlv_op_pull_ch_info_ev, + .pull_peer_delete_resp = ath10k_wmi_tlv_op_pull_peer_delete_ev, .pull_vdev_start = ath10k_wmi_tlv_op_pull_vdev_start_ev, .pull_peer_kick = ath10k_wmi_tlv_op_pull_peer_kick_ev, .pull_swba = ath10k_wmi_tlv_op_pull_swba_ev, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 86b24c24d9f1..500d1d8f441f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -898,6 +898,7 @@ enum wmi_tlv_tag { WMI_TLV_TAG_STRUCT_HL_1_0_SVC_OFFSET = 176, WMI_TLV_TAG_STRUCT_MGMT_TX_CMD = 0x1A6, + WMI_TLV_TAG_STRUCT_PEER_DELETE_RESP_EVENT = 0x1C3, WMI_TLV_TAG_MAX }; diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index d60e7fbb7e74..d6ec0de63582 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2269,6 +2269,24 @@ static bool ath10k_wmi_rx_is_decrypted(struct ath10k *ar, return true; } +int ath10k_wmi_tlv_event_peer_delete_resp(struct ath10k *ar, + struct sk_buff *skb) +{ + int ret; + struct wmi_peer_delete_resp_ev_arg arg = {}; + + ret = ath10k_wmi_pull_peer_delete_resp(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse peer delete resp: %d\n", ret); + dev_kfree_skb(skb); + return ret; + } + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TLV_PEER_DELETE_RESP_EVENTID\n"); + complete(&ar->peer_delete_done); + + return 0; +} + int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_mgmt_rx_ev_arg arg = {}; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 9bd374910379..7ae07a505c59 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6234,6 +6234,11 @@ struct wmi_scan_ev_arg { __le32 vdev_id; }; +struct wmi_peer_delete_resp_ev_arg { + __le32 vdev_id; + struct wmi_mac_addr peer_addr; +}; + struct wmi_mgmt_rx_ev_arg { __le32 channel; __le32 snr; @@ -6603,6 +6608,8 @@ void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg); int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_tlv_event_peer_delete_resp(struct ath10k *ar, + struct sk_buff *skb); int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 201425e7f9cb..fbc8c9a9014b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1815,8 +1815,6 @@ static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah) static void ar9003_hw_tx99_start(struct ath_hw *ah, u32 qnum) { REG_SET_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR); - REG_SET_BIT(ah, 0x9864, 0x7f000); - REG_SET_BIT(ah, 0x9924, 0x7f00fe); REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); REG_WRITE(ah, AR_CR, AR_CR_RXD); REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index ac4781f37e78..b4e6304afd40 100644 --- a/drivers/net/wireless/ath/ath9k/tx99.c +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -190,22 +190,27 @@ static ssize_t write_file_tx99(struct file *file, const char __user *user_buf, if (strtobool(buf, &start)) return -EINVAL; + mutex_lock(&sc->mutex); + if (start == sc->tx99_state) { if (!start) - return count; + goto out; ath_dbg(common, XMIT, "Resetting TX99\n"); ath9k_tx99_deinit(sc); } if (!start) { ath9k_tx99_deinit(sc); - return count; + goto out; } r = ath9k_tx99_init(sc); - if (r) + if (r) { + mutex_unlock(&sc->mutex); return r; - + } +out: + mutex_unlock(&sc->mutex); return count; } diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 63bb7686b811..93868095dc3c 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -31,6 +31,12 @@ static bool ignore_reg_hints = true; module_param(ignore_reg_hints, bool, 0444); MODULE_PARM_DESC(ignore_reg_hints, " Ignore OTA regulatory hints (Default: true)"); +#ifdef CONFIG_PM +static struct wiphy_wowlan_support wil_wowlan_support = { + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + #define CHAN60G(_channel, _flags) { \ .band = IEEE80211_BAND_60GHZ, \ .center_freq = 56160 + (2160 * (_channel)), \ @@ -345,12 +351,12 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, wil_dbg_wmi(wil, "Link status for CID %d: {\n" " MCS %d TSF 0x%016llx\n" - " BF status 0x%08x SNR 0x%08x SQI %d%%\n" + " BF status 0x%08x RSSI %d SQI %d%%\n" " Tx Tpt %d goodput %d Rx goodput %d\n" " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n", cid, le16_to_cpu(reply.evt.bf_mcs), le64_to_cpu(reply.evt.tsf), reply.evt.status, - le32_to_cpu(reply.evt.snr_val), + reply.evt.rssi, reply.evt.sqi, le32_to_cpu(reply.evt.tx_tpt), le32_to_cpu(reply.evt.tx_goodput), @@ -384,7 +390,11 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, if (test_bit(wil_status_fwconnected, wil->status)) { sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); - sinfo->signal = reply.evt.sqi; + if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, + wil->fw_capabilities)) + sinfo->signal = reply.evt.rssi; + else + sinfo->signal = reply.evt.sqi; } return rc; @@ -960,7 +970,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf, len, true); - if (len < sizeof(struct ieee80211_mgmt)) + if (len < sizeof(struct ieee80211_hdr_3addr)) return -EINVAL; cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); @@ -1870,7 +1880,7 @@ static void wil_wiphy_init(struct wiphy *wiphy) wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; - /* TODO: figure this out */ + /* may change after reading FW capabilities */ wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; wiphy->cipher_suites = wil_cipher_suites; @@ -1887,6 +1897,10 @@ static void wil_wiphy_init(struct wiphy *wiphy) wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS; wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; } + +#ifdef CONFIG_PM + wiphy->wowlan = &wil_wowlan_support; +#endif } struct wireless_dev *wil_cfg80211_init(struct device *dev) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 6eefb9e61ec4..f5f3b0468bf8 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1027,6 +1027,7 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data) " TSF = 0x%016llx\n" " TxMCS = %2d TxTpt = %4d\n" " SQI = %4d\n" + " RSSI = %4d\n" " Status = 0x%08x %s\n" " Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n" " Goodput(rx:tx) %4d:%4d\n" @@ -1036,6 +1037,7 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data) le16_to_cpu(reply.evt.bf_mcs), le32_to_cpu(reply.evt.tx_tpt), reply.evt.sqi, + reply.evt.rssi, status, wil_bfstatus_str(status), le16_to_cpu(reply.evt.my_rx_sector), le16_to_cpu(reply.evt.my_tx_sector), @@ -1626,6 +1628,8 @@ static ssize_t wil_write_suspend_stats(struct file *file, struct wil6210_priv *wil = file->private_data; memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats)); + wil->suspend_stats.min_suspend_time = ULONG_MAX; + wil->suspend_stats.collection_start = ktime_get(); return len; } @@ -1637,18 +1641,27 @@ static ssize_t wil_read_suspend_stats(struct file *file, struct wil6210_priv *wil = file->private_data; static char text[400]; int n; + unsigned long long stats_collection_time = + ktime_to_us(ktime_sub(ktime_get(), + wil->suspend_stats.collection_start)); n = snprintf(text, sizeof(text), "Suspend statistics:\n" "successful suspends:%ld failed suspends:%ld\n" "successful resumes:%ld failed resumes:%ld\n" - "rejected by host:%ld rejected by device:%ld\n", + "rejected by host:%ld rejected by device:%ld\n" + "total suspend time:%lld min suspend time:%lld\n" + "max suspend time:%lld stats collection time: %lld\n", wil->suspend_stats.successful_suspends, wil->suspend_stats.failed_suspends, wil->suspend_stats.successful_resumes, wil->suspend_stats.failed_resumes, wil->suspend_stats.rejected_by_host, - wil->suspend_stats.rejected_by_device); + wil->suspend_stats.rejected_by_device, + wil->suspend_stats.total_suspend_time, + wil->suspend_stats.min_suspend_time, + wil->suspend_stats.max_suspend_time, + stats_collection_time); n = min_t(int, n, sizeof(text)); @@ -1804,6 +1817,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) wil6210_debugfs_create_ITR_CNT(wil, dbg); + wil->suspend_stats.collection_start = ktime_get(); + return 0; } diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 78091b7910c7..c48988709631 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -395,10 +395,11 @@ static void wil_fw_error_worker(struct work_struct *work) struct wil6210_priv *wil = container_of(work, struct wil6210_priv, fw_error_worker); struct wireless_dev *wdev = wil->wdev; + struct net_device *ndev = wil_to_ndev(wil); wil_dbg_misc(wil, "fw error worker\n"); - if (!netif_running(wil_to_ndev(wil))) { + if (!(ndev->flags & IFF_UP)) { wil_info(wil, "No recovery - interface is down\n"); return; } @@ -581,6 +582,8 @@ int wil_priv_init(struct wil6210_priv *wil) wil->wakeup_trigger = WMI_WAKEUP_TRIGGER_UCAST | WMI_WAKEUP_TRIGGER_BCAST; + memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats)); + wil->suspend_stats.min_suspend_time = ULONG_MAX; return 0; diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index e91cddbacf24..54aef15a9206 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -84,6 +84,9 @@ void wil_set_capabilities(struct wil6210_priv *wil) /* extract FW capabilities from file without loading the FW */ wil_request_firmware(wil, wil->wil_fw_name, false); + + if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities)) + wil_to_wiphy(wil)->signal_type = CFG80211_SIGNAL_TYPE_MBM; } void wil_disable_irq(struct wil6210_priv *wil) diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index ce1f384e7f8e..8f5d1b447aaa 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -21,10 +21,11 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) { int rc = 0; struct wireless_dev *wdev = wil->wdev; + struct net_device *ndev = wil_to_ndev(wil); wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system"); - if (!netif_running(wil_to_ndev(wil))) { + if (!(ndev->flags & IFF_UP)) { /* can always sleep when down */ wil_dbg_pm(wil, "Interface is down\n"); goto out; @@ -85,7 +86,9 @@ static int wil_resume_keep_radio_on(struct wil6210_priv *wil) /* Send WMI resume request to the device */ rc = wmi_resume(wil); if (rc) { - wil_err(wil, "device failed to resume (%d), resetting\n", rc); + wil_err(wil, "device failed to resume (%d)\n", rc); + if (no_fw_recovery) + goto out; rc = wil_down(wil); if (rc) { wil_err(wil, "wil_down failed (%d)\n", rc); @@ -298,6 +301,9 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime) wil_dbg_pm(wil, "suspend: %s => %d\n", is_runtime ? "runtime" : "system", rc); + if (!rc) + wil->suspend_stats.suspend_start_time = ktime_get(); + return rc; } @@ -307,6 +313,7 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime) struct net_device *ndev = wil_to_ndev(wil); bool keep_radio_on = ndev->flags & IFF_UP && wil->keep_radio_on_during_sleep; + unsigned long long suspend_time_usec = 0; wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); @@ -324,8 +331,20 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime) else rc = wil_resume_radio_off(wil); + if (rc) + goto out; + + suspend_time_usec = + ktime_to_us(ktime_sub(ktime_get(), + wil->suspend_stats.suspend_start_time)); + wil->suspend_stats.total_suspend_time += suspend_time_usec; + if (suspend_time_usec < wil->suspend_stats.min_suspend_time) + wil->suspend_stats.min_suspend_time = suspend_time_usec; + if (suspend_time_usec > wil->suspend_stats.max_suspend_time) + wil->suspend_stats.max_suspend_time = suspend_time_usec; + out: - wil_dbg_pm(wil, "resume: %s => %d\n", - is_runtime ? "runtime" : "system", rc); + wil_dbg_pm(wil, "resume: %s => %d, suspend time %lld usec\n", + is_runtime ? "runtime" : "system", rc, suspend_time_usec); return rc; } diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c index b4c4d09011b4..b91bf51be767 100644 --- a/drivers/net/wireless/ath/wil6210/sysfs.c +++ b/drivers/net/wireless/ath/wil6210/sysfs.c @@ -291,6 +291,8 @@ int wil6210_sysfs_init(struct wil6210_priv *wil) return err; } + kobject_uevent(&dev->kobj, KOBJ_CHANGE); + return 0; } @@ -299,4 +301,5 @@ void wil6210_sysfs_remove(struct wil6210_priv *wil) struct device *dev = wil_to_dev(wil); sysfs_remove_group(&dev->kobj, &wil6210_attribute_group); + kobject_uevent(&dev->kobj, KOBJ_CHANGE); } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 9525f521d215..3fe62d8caaa4 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -91,6 +91,11 @@ struct wil_suspend_stats { unsigned long failed_resumes; unsigned long rejected_by_device; unsigned long rejected_by_host; + unsigned long long total_suspend_time; + unsigned long long min_suspend_time; + unsigned long long max_suspend_time; + ktime_t collection_start; + ktime_t suspend_start_time; }; /* Calculate MAC buffer size for the firmware. It includes all overhead, diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index fba0d6a79ae2..61f629c94bbb 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -382,12 +382,15 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) ch_no = data->info.channel + 1; freq = ieee80211_channel_to_frequency(ch_no, IEEE80211_BAND_60GHZ); channel = ieee80211_get_channel(wiphy, freq); - signal = data->info.sqi; + if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities)) + signal = 100 * data->info.rssi; + else + signal = data->info.sqi; d_status = le16_to_cpu(data->info.status); fc = rx_mgmt_frame->frame_control; - wil_dbg_wmi(wil, "MGMT Rx: channel %d MCS %d SNR %d SQI %d%%\n", - data->info.channel, data->info.mcs, data->info.snr, + wil_dbg_wmi(wil, "MGMT Rx: channel %d MCS %d RSSI %d SQI %d%%\n", + data->info.channel, data->info.mcs, data->info.rssi, data->info.sqi); wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len, le16_to_cpu(fc)); diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 256f63c57da0..4e31c2fd1fc6 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -60,6 +60,7 @@ enum wmi_fw_capability { WMI_FW_CAPABILITY_WMI_ONLY = 5, WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7, WMI_FW_CAPABILITY_D3_SUSPEND = 8, + WMI_FW_CAPABILITY_RSSI_REPORTING = 12, WMI_FW_CAPABILITY_MAX, }; @@ -1306,7 +1307,8 @@ struct wmi_notify_req_done_event { /* beamforming status, 0: fail; 1: OK; 2: retrying */ __le32 status; __le64 tsf; - __le32 snr_val; + s8 rssi; + u8 reserved0[3]; __le32 tx_tpt; __le32 tx_goodput; __le32 rx_goodput; @@ -1602,7 +1604,7 @@ struct wmi_get_ssid_event { /* wmi_rx_mgmt_info */ struct wmi_rx_mgmt_info { u8 mcs; - s8 snr; + s8 rssi; u8 range; u8 sqi; __le16 stype; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 70a6985334d5..da5826d788d6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -4472,6 +4472,11 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, GFP_KERNEL); } else if (ieee80211_is_action(mgmt->frame_control)) { + if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) { + brcmf_err("invalid action frame length\n"); + err = -EINVAL; + goto exit; + } af_params = kzalloc(sizeof(*af_params), GFP_KERNEL); if (af_params == NULL) { brcmf_err("unable to allocate frame\n"); diff --git a/drivers/net/wireless/cnss/Kconfig b/drivers/net/wireless/cnss/Kconfig index 6faf9f1ef5d0..863f766bccdb 100644 --- a/drivers/net/wireless/cnss/Kconfig +++ b/drivers/net/wireless/cnss/Kconfig @@ -46,7 +46,6 @@ config CLD_HL_SDIO_CORE select WEXT_CORE select WEXT_SPY select NL80211_TESTMODE - depends on ARCH_MSM depends on MMC config CLD_LL_CORE diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index 22c59d8c3c45..af92f00ca56e 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -83,6 +83,7 @@ #define QCA6180_DEVICE_ID (0x0041) #define QCA6180_REV_ID_OFFSET (0x08) +#define WLAN_EN_VREG_NAME "vdd-wlan-en" #define WLAN_VREG_NAME "vdd-wlan" #define WLAN_VREG_IO_NAME "vdd-wlan-io" #define WLAN_VREG_XTAL_NAME "vdd-wlan-xtal" @@ -114,6 +115,8 @@ #define WLAN_VREG_SP2T_MIN 2700000 #define POWER_ON_DELAY 2 +#define WLAN_VREG_IO_DELAY_MIN 100 +#define WLAN_VREG_IO_DELAY_MAX 1000 #define WLAN_ENABLE_DELAY 10 #define WLAN_RECOVERY_DELAY 1 #define PCIE_ENABLE_DELAY 100 @@ -134,7 +137,6 @@ static DEFINE_SPINLOCK(pci_link_down_lock); #define FW_IMAGE_MISSION (0x02) #define FW_IMAGE_BDATA (0x03) #define FW_IMAGE_PRINT (0x04) -#define FW_SETUP_DELAY 2000 #define SEG_METADATA (0x01) #define SEG_NON_PAGED (0x02) @@ -155,6 +157,7 @@ struct cnss_wlan_gpio_info { }; struct cnss_wlan_vreg_info { + struct regulator *wlan_en_reg; struct regulator *wlan_reg; struct regulator *soc_swreg; struct regulator *ant_switch; @@ -237,6 +240,7 @@ static struct cnss_data { dma_addr_t smmu_iova_start; size_t smmu_iova_len; struct cnss_wlan_vreg_info vreg_info; + bool wlan_en_vreg_support; struct cnss_wlan_gpio_info gpio_info; bool pcie_link_state; bool pcie_link_down_ind; @@ -274,10 +278,8 @@ static struct cnss_data { u32 fw_dma_size; u32 fw_seg_count; struct segment_memory fw_seg_mem[MAX_NUM_OF_SEGMENTS]; - atomic_t fw_store_in_progress; /* Firmware setup complete lock */ struct mutex fw_setup_stat_lock; - struct completion fw_setup_complete; void *bdata_cpu; dma_addr_t bdata_dma; u32 bdata_dma_size; @@ -294,6 +296,17 @@ module_param(pcie_link_down_panic, uint, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(pcie_link_down_panic, "Trigger kernel panic when PCIe link down is detected"); +static void cnss_put_wlan_enable_gpio(void) +{ + struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info; + struct cnss_wlan_vreg_info *vreg_info = &penv->vreg_info; + + if (penv->wlan_en_vreg_support) + regulator_put(vreg_info->wlan_en_reg); + else + gpio_free(gpio_info->num); +} + static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info) { int ret; @@ -307,13 +320,6 @@ static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info) } } - ret = regulator_enable(vreg_info->wlan_reg); - if (ret) { - pr_err("%s: regulator enable failed for WLAN power\n", - __func__); - goto error_enable; - } - if (vreg_info->wlan_reg_io) { ret = regulator_enable(vreg_info->wlan_reg_io); if (ret) { @@ -321,6 +327,8 @@ static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info) __func__); goto error_enable_reg_io; } + + usleep_range(WLAN_VREG_IO_DELAY_MIN, WLAN_VREG_IO_DELAY_MAX); } if (vreg_info->wlan_reg_xtal_aon) { @@ -341,6 +349,13 @@ static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info) } } + ret = regulator_enable(vreg_info->wlan_reg); + if (ret) { + pr_err("%s: regulator enable failed for WLAN power\n", + __func__); + goto error_enable; + } + if (vreg_info->wlan_reg_sp2t) { ret = regulator_enable(vreg_info->wlan_reg_sp2t); if (ret) { @@ -377,6 +392,8 @@ error_enable_ant_switch: if (vreg_info->wlan_reg_sp2t) regulator_disable(vreg_info->wlan_reg_sp2t); error_enable_reg_sp2t: + regulator_disable(vreg_info->wlan_reg); +error_enable: if (vreg_info->wlan_reg_xtal) regulator_disable(vreg_info->wlan_reg_xtal); error_enable_reg_xtal: @@ -386,8 +403,6 @@ error_enable_reg_xtal_aon: if (vreg_info->wlan_reg_io) regulator_disable(vreg_info->wlan_reg_io); error_enable_reg_io: - regulator_disable(vreg_info->wlan_reg); -error_enable: if (vreg_info->wlan_reg_core) regulator_disable(vreg_info->wlan_reg_core); error_enable_reg_core: @@ -425,6 +440,13 @@ static int cnss_wlan_vreg_off(struct cnss_wlan_vreg_info *vreg_info) } } + ret = regulator_disable(vreg_info->wlan_reg); + if (ret) { + pr_err("%s: regulator disable failed for WLAN power\n", + __func__); + goto error_disable; + } + if (vreg_info->wlan_reg_xtal) { ret = regulator_disable(vreg_info->wlan_reg_xtal); if (ret) { @@ -452,13 +474,6 @@ static int cnss_wlan_vreg_off(struct cnss_wlan_vreg_info *vreg_info) } } - ret = regulator_disable(vreg_info->wlan_reg); - if (ret) { - pr_err("%s: regulator disable failed for WLAN power\n", - __func__); - goto error_disable; - } - if (vreg_info->wlan_reg_core) { ret = regulator_disable(vreg_info->wlan_reg_core); if (ret) { @@ -575,6 +590,25 @@ static void cnss_wlan_gpio_set(struct cnss_wlan_gpio_info *info, bool state) info->name, info->state ? "enabled" : "disabled"); } +static int cnss_configure_wlan_en_gpio(bool state) +{ + int ret = 0; + struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info; + struct cnss_wlan_vreg_info *vreg_info = &penv->vreg_info; + + if (penv->wlan_en_vreg_support) { + if (state) + ret = regulator_enable(vreg_info->wlan_en_reg); + else + ret = regulator_disable(vreg_info->wlan_en_reg); + } else { + cnss_wlan_gpio_set(gpio_info, state); + } + + msleep(WLAN_ENABLE_DELAY); + return ret; +} + static int cnss_pinctrl_init(struct cnss_wlan_gpio_info *gpio_info, struct platform_device *pdev) { @@ -681,14 +715,71 @@ end: return ret; } +static int cnss_get_wlan_enable_gpio( + struct cnss_wlan_gpio_info *gpio_info, + struct platform_device *pdev) +{ + int ret = 0; + struct device *dev = &pdev->dev; + + if (!of_find_property(dev->of_node, gpio_info->name, NULL)) { + gpio_info->prop = false; + return -ENODEV; + } + + gpio_info->prop = true; + ret = of_get_named_gpio(dev->of_node, gpio_info->name, 0); + if (ret >= 0) { + gpio_info->num = ret; + } else { + if (ret == -EPROBE_DEFER) + pr_debug("get WLAN_EN GPIO probe defer\n"); + else + pr_err( + "can't get gpio %s ret %d", gpio_info->name, ret); + } + + ret = cnss_pinctrl_init(gpio_info, pdev); + if (ret) + pr_debug("%s: pinctrl init failed!\n", __func__); + + ret = cnss_wlan_gpio_init(gpio_info); + if (ret) + pr_err("gpio init failed\n"); + + return ret; +} + +static int cnss_get_wlan_bootstrap_gpio(struct platform_device *pdev) +{ + int ret = 0; + struct device_node *node = (&pdev->dev)->of_node; + + if (!of_find_property(node, WLAN_BOOTSTRAP_GPIO_NAME, NULL)) + return ret; + + penv->wlan_bootstrap_gpio = + of_get_named_gpio(node, WLAN_BOOTSTRAP_GPIO_NAME, 0); + if (penv->wlan_bootstrap_gpio > 0) { + ret = cnss_wlan_bootstrap_gpio_init(); + } else { + ret = penv->wlan_bootstrap_gpio; + pr_err( + "%s: Can't get GPIO %s, ret = %d", + __func__, WLAN_BOOTSTRAP_GPIO_NAME, ret); + } + + return ret; +} + static int cnss_wlan_get_resources(struct platform_device *pdev) { int ret = 0; struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info; struct cnss_wlan_vreg_info *vreg_info = &penv->vreg_info; + struct device_node *node = pdev->dev.of_node; - if (of_get_property(pdev->dev.of_node, - WLAN_VREG_CORE_NAME"-supply", NULL)) { + if (of_get_property(node, WLAN_VREG_CORE_NAME "-supply", NULL)) { vreg_info->wlan_reg_core = regulator_get(&pdev->dev, WLAN_VREG_CORE_NAME); if (IS_ERR(vreg_info->wlan_reg_core)) { @@ -718,26 +809,7 @@ static int cnss_wlan_get_resources(struct platform_device *pdev) } } - vreg_info->wlan_reg = regulator_get(&pdev->dev, WLAN_VREG_NAME); - - if (IS_ERR(vreg_info->wlan_reg)) { - if (PTR_ERR(vreg_info->wlan_reg) == -EPROBE_DEFER) - pr_err("%s: vreg probe defer\n", __func__); - else - pr_err("%s: vreg regulator get failed\n", __func__); - ret = PTR_ERR(vreg_info->wlan_reg); - goto err_reg_get; - } - - ret = regulator_enable(vreg_info->wlan_reg); - - if (ret) { - pr_err("%s: vreg initial vote failed\n", __func__); - goto err_reg_enable; - } - - if (of_get_property(pdev->dev.of_node, - WLAN_VREG_IO_NAME"-supply", NULL)) { + if (of_get_property(node, WLAN_VREG_IO_NAME "-supply", NULL)) { vreg_info->wlan_reg_io = regulator_get(&pdev->dev, WLAN_VREG_IO_NAME); if (!IS_ERR(vreg_info->wlan_reg_io)) { @@ -755,14 +827,34 @@ static int cnss_wlan_get_resources(struct platform_device *pdev) __func__); goto err_reg_io_enable; } + + usleep_range(WLAN_VREG_IO_DELAY_MIN, + WLAN_VREG_IO_DELAY_MAX); } } if (cnss_enable_xtal_ldo(pdev)) goto err_reg_xtal_enable; - if (of_get_property(pdev->dev.of_node, - WLAN_VREG_SP2T_NAME"-supply", NULL)) { + vreg_info->wlan_reg = regulator_get(&pdev->dev, WLAN_VREG_NAME); + + if (IS_ERR(vreg_info->wlan_reg)) { + if (PTR_ERR(vreg_info->wlan_reg) == -EPROBE_DEFER) + pr_err("%s: vreg probe defer\n", __func__); + else + pr_err("%s: vreg regulator get failed\n", __func__); + ret = PTR_ERR(vreg_info->wlan_reg); + goto err_reg_get; + } + + ret = regulator_enable(vreg_info->wlan_reg); + + if (ret) { + pr_err("%s: vreg initial vote failed\n", __func__); + goto err_reg_enable; + } + + if (of_get_property(node, WLAN_VREG_SP2T_NAME "-supply", NULL)) { vreg_info->wlan_reg_sp2t = regulator_get(&pdev->dev, WLAN_VREG_SP2T_NAME); if (!IS_ERR(vreg_info->wlan_reg_sp2t)) { @@ -783,8 +875,7 @@ static int cnss_wlan_get_resources(struct platform_device *pdev) } } - if (of_get_property(pdev->dev.of_node, - WLAN_ANT_SWITCH_NAME "-supply", NULL)) { + if (of_get_property(node, WLAN_ANT_SWITCH_NAME "-supply", NULL)) { vreg_info->ant_switch = regulator_get(&pdev->dev, WLAN_ANT_SWITCH_NAME); if (!IS_ERR(vreg_info->ant_switch)) { @@ -814,13 +905,10 @@ static int cnss_wlan_get_resources(struct platform_device *pdev) } } - if (of_find_property((&pdev->dev)->of_node, - "qcom,wlan-uart-access", NULL)) + if (of_find_property(node, "qcom,wlan-uart-access", NULL)) penv->cap.cap_flag |= CNSS_HAS_UART_ACCESS; - if (of_get_property(pdev->dev.of_node, - WLAN_SWREG_NAME"-supply", NULL)) { - + if (of_get_property(node, WLAN_SWREG_NAME "-supply", NULL)) { vreg_info->soc_swreg = regulator_get(&pdev->dev, WLAN_SWREG_NAME); if (IS_ERR(vreg_info->soc_swreg)) { @@ -843,68 +931,41 @@ static int cnss_wlan_get_resources(struct platform_device *pdev) penv->cap.cap_flag |= CNSS_HAS_EXTERNAL_SWREG; } - vreg_info->state = VREG_ON; - - if (!of_find_property((&pdev->dev)->of_node, gpio_info->name, NULL)) { - gpio_info->prop = false; - goto end; + penv->wlan_en_vreg_support = + of_property_read_bool(node, "qcom,wlan-en-vreg-support"); + if (penv->wlan_en_vreg_support) { + vreg_info->wlan_en_reg = + regulator_get(&pdev->dev, WLAN_EN_VREG_NAME); + if (IS_ERR(vreg_info->wlan_en_reg)) { + pr_err("%s:wlan_en vreg get failed\n", __func__); + ret = PTR_ERR(vreg_info->wlan_en_reg); + goto err_wlan_en_reg_get; + } } - gpio_info->prop = true; - ret = of_get_named_gpio((&pdev->dev)->of_node, - gpio_info->name, 0); - - if (ret >= 0) { - gpio_info->num = ret; - ret = 0; - } else { - if (ret == -EPROBE_DEFER) - pr_debug("get WLAN_EN GPIO probe defer\n"); - else - pr_err("can't get gpio %s ret %d", - gpio_info->name, ret); - goto err_get_gpio; + if (!penv->wlan_en_vreg_support) { + ret = cnss_get_wlan_enable_gpio(gpio_info, pdev); + if (ret) { + pr_err( + "%s:Failed to config the WLAN_EN gpio\n", __func__); + goto err_gpio_wlan_en; + } } + vreg_info->state = VREG_ON; - ret = cnss_pinctrl_init(gpio_info, pdev); + ret = cnss_get_wlan_bootstrap_gpio(pdev); if (ret) { - pr_err("%s: pinctrl init failed!\n", __func__); - goto err_pinctrl_init; + pr_err("%s: Failed to enable wlan bootstrap gpio\n", __func__); + goto err_gpio_wlan_bootstrap; } - ret = cnss_wlan_gpio_init(gpio_info); - if (ret) { - pr_err("gpio init failed\n"); - goto err_gpio_init; - } - - if (of_find_property((&pdev->dev)->of_node, - WLAN_BOOTSTRAP_GPIO_NAME, NULL)) { - penv->wlan_bootstrap_gpio = - of_get_named_gpio((&pdev->dev)->of_node, - WLAN_BOOTSTRAP_GPIO_NAME, 0); - if (penv->wlan_bootstrap_gpio > 0) { - ret = cnss_wlan_bootstrap_gpio_init(); - if (ret) - goto err_gpio_init; - } else { - if (ret == -EPROBE_DEFER) { - pr_debug("%s: Get GPIO %s probe defer\n", - __func__, WLAN_BOOTSTRAP_GPIO_NAME); - } else { - pr_err("%s: Can't get GPIO %s, ret = %d", - __func__, WLAN_BOOTSTRAP_GPIO_NAME, - ret); - } - goto err_gpio_init; - } - } -end: return ret; -err_gpio_init: -err_pinctrl_init: -err_get_gpio: +err_gpio_wlan_bootstrap: + cnss_put_wlan_enable_gpio(); +err_gpio_wlan_en: +err_wlan_en_reg_get: + vreg_info->wlan_en_reg = NULL; if (vreg_info->soc_swreg) regulator_disable(vreg_info->soc_swreg); vreg_info->state = VREG_OFF; @@ -929,7 +990,11 @@ err_reg_sp2t_enable: err_reg_sp2t_set: if (vreg_info->wlan_reg_sp2t) regulator_put(vreg_info->wlan_reg_sp2t); + regulator_disable(vreg_info->wlan_reg); +err_reg_enable: + regulator_put(vreg_info->wlan_reg); +err_reg_get: cnss_disable_xtal_ldo(pdev); err_reg_xtal_enable: @@ -940,12 +1005,6 @@ err_reg_io_enable: err_reg_io_set: if (vreg_info->wlan_reg_io) regulator_put(vreg_info->wlan_reg_io); - regulator_disable(vreg_info->wlan_reg); - -err_reg_enable: - regulator_put(vreg_info->wlan_reg); - -err_reg_get: if (vreg_info->wlan_reg_core) regulator_disable(vreg_info->wlan_reg_core); @@ -965,7 +1024,7 @@ static void cnss_wlan_release_resources(void) if (penv->wlan_bootstrap_gpio > 0) gpio_free(penv->wlan_bootstrap_gpio); - gpio_free(gpio_info->num); + cnss_put_wlan_enable_gpio(); gpio_info->state = WLAN_EN_LOW; gpio_info->prop = false; cnss_wlan_vreg_set(vreg_info, VREG_OFF); @@ -975,13 +1034,13 @@ static void cnss_wlan_release_resources(void) regulator_put(vreg_info->ant_switch); if (vreg_info->wlan_reg_sp2t) regulator_put(vreg_info->wlan_reg_sp2t); + regulator_put(vreg_info->wlan_reg); if (vreg_info->wlan_reg_xtal) regulator_put(vreg_info->wlan_reg_xtal); if (vreg_info->wlan_reg_xtal_aon) regulator_put(vreg_info->wlan_reg_xtal_aon); if (vreg_info->wlan_reg_io) regulator_put(vreg_info->wlan_reg_io); - regulator_put(vreg_info->wlan_reg); if (vreg_info->wlan_reg_core) regulator_put(vreg_info->wlan_reg_core); vreg_info->state = VREG_OFF; @@ -1374,15 +1433,6 @@ int cnss_get_fw_image(struct image_desc_info *image_desc_info) !penv->fw_seg_count || !penv->bdata_seg_count) return -EINVAL; - /* Check for firmware setup trigger by usersapce is in progress - * and wait for complition of firmware setup. - */ - - if (atomic_read(&penv->fw_store_in_progress)) { - wait_for_completion_timeout(&penv->fw_setup_complete, - msecs_to_jiffies(FW_SETUP_DELAY)); - } - mutex_lock(&penv->fw_setup_stat_lock); image_desc_info->fw_addr = penv->fw_dma; image_desc_info->fw_size = penv->fw_dma_size; @@ -1560,7 +1610,6 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev, { int ret = 0; struct cnss_wlan_vreg_info *vreg_info = &penv->vreg_info; - struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info; void *cpu_addr; dma_addr_t dma_handle; struct codeswap_codeseg_info *cnss_seg_info = NULL; @@ -1619,7 +1668,7 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev, penv->pcie_link_state = PCIE_LINK_DOWN; } - cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW); + cnss_configure_wlan_en_gpio(WLAN_EN_LOW); ret = cnss_wlan_vreg_set(vreg_info, VREG_OFF); if (ret) { @@ -1627,7 +1676,9 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev, goto err_pcie_suspend; } + mutex_lock(&penv->fw_setup_stat_lock); cnss_wlan_fw_mem_alloc(pdev); + mutex_unlock(&penv->fw_setup_stat_lock); ret = device_create_file(&penv->pldev->dev, &dev_attr_wlan_setup); @@ -1874,17 +1925,11 @@ static ssize_t fw_image_setup_store(struct device *dev, if (!penv) return -ENODEV; - if (atomic_read(&penv->fw_store_in_progress)) { - pr_info("%s: Firmware setup in progress\n", __func__); - return 0; - } - - atomic_set(&penv->fw_store_in_progress, 1); - init_completion(&penv->fw_setup_complete); + mutex_lock(&penv->fw_setup_stat_lock); + pr_info("%s: Firmware setup in progress\n", __func__); if (kstrtoint(buf, 0, &val)) { - atomic_set(&penv->fw_store_in_progress, 0); - complete(&penv->fw_setup_complete); + mutex_unlock(&penv->fw_setup_stat_lock); return -EINVAL; } @@ -1895,8 +1940,7 @@ static ssize_t fw_image_setup_store(struct device *dev, if (ret != 0) { pr_err("%s: Invalid parsing of FW image files %d", __func__, ret); - atomic_set(&penv->fw_store_in_progress, 0); - complete(&penv->fw_setup_complete); + mutex_unlock(&penv->fw_setup_stat_lock); return -EINVAL; } penv->fw_image_setup = val; @@ -1906,9 +1950,8 @@ static ssize_t fw_image_setup_store(struct device *dev, penv->bmi_test = val; } - atomic_set(&penv->fw_store_in_progress, 0); - complete(&penv->fw_setup_complete); - + pr_info("%s: Firmware setup completed\n", __func__); + mutex_unlock(&penv->fw_setup_stat_lock); return count; } @@ -2007,16 +2050,21 @@ int cnss_get_codeswap_struct(struct codeswap_codeseg_info *swap_seg) { struct codeswap_codeseg_info *cnss_seg_info = penv->cnss_seg_info; + mutex_lock(&penv->fw_setup_stat_lock); if (!cnss_seg_info) { swap_seg = NULL; + mutex_unlock(&penv->fw_setup_stat_lock); return -ENOENT; } + if (!atomic_read(&penv->fw_available)) { pr_debug("%s: fw is not available\n", __func__); + mutex_unlock(&penv->fw_setup_stat_lock); return -ENOENT; } *swap_seg = *cnss_seg_info; + mutex_unlock(&penv->fw_setup_stat_lock); return 0; } @@ -2035,15 +2083,6 @@ static void cnss_wlan_memory_expansion(void) u_int32_t total_length = 0; struct pci_dev *pdev; - /* Check for firmware setup trigger by usersapce is in progress - * and wait for complition of firmware setup. - */ - - if (atomic_read(&penv->fw_store_in_progress)) { - wait_for_completion_timeout(&penv->fw_setup_complete, - msecs_to_jiffies(FW_SETUP_DELAY)); - } - mutex_lock(&penv->fw_setup_stat_lock); filename = cnss_wlan_get_evicted_data_file(); pdev = penv->pdev; @@ -2276,8 +2315,7 @@ again: msleep(WLAN_BOOTSTRAP_DELAY); } - cnss_wlan_gpio_set(gpio_info, WLAN_EN_HIGH); - msleep(WLAN_ENABLE_DELAY); + cnss_configure_wlan_en_gpio(WLAN_EN_HIGH); if (!pdev) { pr_debug("%s: invalid pdev. register pci device\n", __func__); @@ -2360,8 +2398,7 @@ again: cnss_get_pci_dev_bus_number(pdev), pdev, PM_OPTIONS); penv->pcie_link_state = PCIE_LINK_DOWN; - cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW); - msleep(WLAN_ENABLE_DELAY); + cnss_configure_wlan_en_gpio(WLAN_EN_LOW); cnss_wlan_vreg_set(vreg_info, VREG_OFF); msleep(POWER_ON_DELAY); probe_again++; @@ -2388,7 +2425,7 @@ err_pcie_link_up: } err_pcie_reg: - cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW); + cnss_configure_wlan_en_gpio(WLAN_EN_LOW); cnss_wlan_vreg_set(vreg_info, VREG_OFF); if (penv->pdev) { pr_err("%d: Unregistering PCI device\n", __LINE__); @@ -2469,8 +2506,7 @@ void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver) cut_power: penv->driver = NULL; - cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW); - + cnss_configure_wlan_en_gpio(WLAN_EN_LOW); if (cnss_wlan_vreg_set(vreg_info, VREG_OFF)) pr_err("wlan vreg OFF failed\n"); } @@ -2582,8 +2618,7 @@ static int cnss_shutdown(const struct subsys_desc *subsys, bool force_stop) } cut_power: - cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW); - + cnss_configure_wlan_en_gpio(WLAN_EN_LOW); if (cnss_wlan_vreg_set(vreg_info, VREG_OFF)) pr_err("cnss: Failed to set WLAN VREG_OFF!\n"); @@ -2616,8 +2651,7 @@ static int cnss_powerup(const struct subsys_desc *subsys) } msleep(POWER_ON_DELAY); - cnss_wlan_gpio_set(gpio_info, WLAN_EN_HIGH); - msleep(WLAN_ENABLE_DELAY); + cnss_configure_wlan_en_gpio(WLAN_EN_HIGH); if (!pdev) { pr_err("%d: invalid pdev\n", __LINE__); @@ -2677,7 +2711,7 @@ err_wlan_reinit: penv->pcie_link_state = PCIE_LINK_DOWN; err_pcie_link_up: - cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW); + cnss_configure_wlan_en_gpio(WLAN_EN_LOW); cnss_wlan_vreg_set(vreg_info, VREG_OFF); if (penv->pdev) { pr_err("%d: Unregistering pci device\n", __LINE__); @@ -2859,13 +2893,17 @@ static int cnss_probe(struct platform_device *pdev) penv->vreg_info.wlan_reg = NULL; penv->vreg_info.state = VREG_OFF; penv->pci_register_again = false; + mutex_init(&penv->fw_setup_stat_lock); ret = cnss_wlan_get_resources(pdev); if (ret) goto err_get_wlan_res; - cnss_wlan_gpio_set(&penv->gpio_info, WLAN_EN_HIGH); - msleep(WLAN_ENABLE_DELAY); + ret = cnss_configure_wlan_en_gpio(WLAN_EN_HIGH); + if (ret) { + pr_err("%s: Failed to enable WLAN enable gpio\n", __func__); + goto err_get_rc; + } ret = of_property_read_u32(dev->of_node, "qcom,wlan-rc-num", &rc_num); if (ret) { @@ -3016,8 +3054,6 @@ skip_ramdump: memset(phys_to_virt(0), 0, SZ_4K); #endif - atomic_set(&penv->fw_store_in_progress, 0); - mutex_init(&penv->fw_setup_stat_lock); ret = device_create_file(dev, &dev_attr_fw_image_setup); if (ret) { pr_err("cnss: fw_image_setup sys file creation failed\n"); @@ -3062,7 +3098,7 @@ err_subsys_reg: err_esoc_reg: err_pcie_enumerate: err_get_rc: - cnss_wlan_gpio_set(&penv->gpio_info, WLAN_EN_LOW); + cnss_configure_wlan_en_gpio(WLAN_EN_LOW); cnss_wlan_release_resources(); err_get_wlan_res: @@ -3073,8 +3109,6 @@ err_get_wlan_res: static int cnss_remove(struct platform_device *pdev) { - struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info; - unregister_pm_notifier(&cnss_pm_notifier); device_remove_file(&pdev->dev, &dev_attr_fw_image_setup); @@ -3095,7 +3129,7 @@ static int cnss_remove(struct platform_device *pdev) } } - cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW); + cnss_configure_wlan_en_gpio(WLAN_EN_LOW); if (penv->wlan_bootstrap_gpio > 0) gpio_set_value(penv->wlan_bootstrap_gpio, WLAN_BOOTSTRAP_LOW); cnss_wlan_release_resources(); @@ -3596,8 +3630,7 @@ static int __cnss_pcie_power_up(struct device *dev) msleep(WLAN_BOOTSTRAP_DELAY); } - cnss_wlan_gpio_set(gpio_info, WLAN_EN_HIGH); - msleep(WLAN_ENABLE_DELAY); + cnss_configure_wlan_en_gpio(WLAN_EN_HIGH); return 0; } @@ -3610,8 +3643,7 @@ static int __cnss_pcie_power_down(struct device *dev) vreg_info = &penv->vreg_info; gpio_info = &penv->gpio_info; - cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW); - + cnss_configure_wlan_en_gpio(WLAN_EN_LOW); if (penv->wlan_bootstrap_gpio > 0) gpio_set_value(penv->wlan_bootstrap_gpio, WLAN_BOOTSTRAP_LOW); diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c index ce7dbc64c4c3..8586bb16cfd3 100644 --- a/drivers/net/wireless/cnss/cnss_sdio.c +++ b/drivers/net/wireless/cnss/cnss_sdio.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -276,6 +276,11 @@ static int cnss_get_hw_resources(struct device *dev) host = info->host; + if (!host) { + pr_err("%s: MMC host is invalid\n", __func__); + return -ENODEV; + } + ret = regulator_enable(cnss_pdata->regulator.wlan_vreg); if (ret) { pr_err("%s: Failed to enable wlan vreg\n", __func__); diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c index 360ab31c61dd..35d7fe1c318c 100644 --- a/drivers/net/wireless/cnss2/debug.c +++ b/drivers/net/wireless/cnss2/debug.c @@ -15,6 +15,7 @@ #include <linux/debugfs.h> #include "main.h" #include "debug.h" +#include "pci.h" #define CNSS_IPC_LOG_PAGES 32 @@ -97,6 +98,10 @@ static int cnss_stats_show_state(struct seq_file *s, continue; case CNSS_DEV_ERR_NOTIFY: seq_puts(s, "DEV_ERR"); + continue; + case CNSS_DRIVER_DEBUG: + seq_puts(s, "DRIVER_DEBUG"); + continue; } seq_printf(s, "UNKNOWN-%d", i); @@ -121,13 +126,323 @@ static int cnss_stats_open(struct inode *inode, struct file *file) } static const struct file_operations cnss_stats_fops = { - .read = seq_read, - .release = single_release, - .open = cnss_stats_open, - .owner = THIS_MODULE, - .llseek = seq_lseek, + .read = seq_read, + .release = single_release, + .open = cnss_stats_open, + .owner = THIS_MODULE, + .llseek = seq_lseek, +}; + +static ssize_t cnss_dev_boot_debug_write(struct file *fp, + const char __user *user_buf, + size_t count, loff_t *off) +{ + struct cnss_plat_data *plat_priv = + ((struct seq_file *)fp->private_data)->private; + char buf[64]; + char *cmd; + unsigned int len = 0; + int ret = 0; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + cmd = buf; + + if (sysfs_streq(cmd, "on")) { + ret = cnss_power_on_device(plat_priv); + } else if (sysfs_streq(cmd, "off")) { + cnss_power_off_device(plat_priv); + } else if (sysfs_streq(cmd, "enumerate")) { + ret = cnss_pci_init(plat_priv); + } else if (sysfs_streq(cmd, "download")) { + ret = cnss_pci_start_mhi(plat_priv->bus_priv); + } else if (sysfs_streq(cmd, "linkup")) { + ret = cnss_resume_pci_link(plat_priv->bus_priv); + } else if (sysfs_streq(cmd, "linkdown")) { + ret = cnss_suspend_pci_link(plat_priv->bus_priv); + } else if (sysfs_streq(cmd, "powerup")) { + set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state); + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_POWER_UP, + true, NULL); + } else if (sysfs_streq(cmd, "shutdown")) { + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_POWER_DOWN, + true, NULL); + clear_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state); + } else { + cnss_pr_err("Device boot debugfs command is invalid\n"); + ret = -EINVAL; + } + + if (ret) + return ret; + + return count; +} + +static int cnss_dev_boot_debug_show(struct seq_file *s, void *data) +{ + seq_puts(s, "\nUsage: echo <action> > <debugfs_path>/cnss/dev_boot\n"); + seq_puts(s, "<action> can be one of below:\n"); + seq_puts(s, "on: turn on device power, assert WLAN_EN\n"); + seq_puts(s, "off: de-assert WLAN_EN, turn off device power\n"); + seq_puts(s, "enumerate: de-assert PERST, enumerate PCIe\n"); + seq_puts(s, "download: download FW and do QMI handshake with FW\n"); + seq_puts(s, "linkup: bring up PCIe link\n"); + seq_puts(s, "linkdown: bring down PCIe link\n"); + seq_puts(s, "powerup: full power on sequence to boot device, download FW and do QMI handshake with FW\n"); + seq_puts(s, "shutdown: full power off sequence to shutdown device\n"); + + return 0; +} + +static int cnss_dev_boot_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, cnss_dev_boot_debug_show, inode->i_private); +} + +static const struct file_operations cnss_dev_boot_debug_fops = { + .read = seq_read, + .write = cnss_dev_boot_debug_write, + .release = single_release, + .open = cnss_dev_boot_debug_open, + .owner = THIS_MODULE, + .llseek = seq_lseek, +}; + +static int cnss_reg_read_debug_show(struct seq_file *s, void *data) +{ + struct cnss_plat_data *plat_priv = s->private; + + mutex_lock(&plat_priv->dev_lock); + if (!plat_priv->diag_reg_read_buf) { + seq_puts(s, "\nUsage: echo <mem_type> <offset> <data_len> > <debugfs_path>/cnss/reg_read\n"); + mutex_unlock(&plat_priv->dev_lock); + return 0; + } + + seq_printf(s, "\nRegister read, address: 0x%x memory type: 0x%x length: 0x%x\n\n", + plat_priv->diag_reg_read_addr, + plat_priv->diag_reg_read_mem_type, + plat_priv->diag_reg_read_len); + + seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, + plat_priv->diag_reg_read_buf, + plat_priv->diag_reg_read_len, false); + + plat_priv->diag_reg_read_len = 0; + kfree(plat_priv->diag_reg_read_buf); + plat_priv->diag_reg_read_buf = NULL; + mutex_unlock(&plat_priv->dev_lock); + + return 0; +} + +static ssize_t cnss_reg_read_debug_write(struct file *fp, + const char __user *user_buf, + size_t count, loff_t *off) +{ + struct cnss_plat_data *plat_priv = + ((struct seq_file *)fp->private_data)->private; + char buf[64]; + char *sptr, *token; + unsigned int len = 0; + u32 reg_offset, mem_type; + u32 data_len = 0; + u8 *reg_buf = NULL; + const char *delim = " "; + int ret = 0; + + if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + cnss_pr_err("Firmware is not ready yet\n"); + return -EINVAL; + } + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + + if (!sptr) + return -EINVAL; + + if (kstrtou32(token, 0, &mem_type)) + return -EINVAL; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + + if (!sptr) + return -EINVAL; + + if (kstrtou32(token, 0, ®_offset)) + return -EINVAL; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + + if (kstrtou32(token, 0, &data_len)) + return -EINVAL; + + if (data_len == 0 || + data_len > QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01) + return -EINVAL; + + mutex_lock(&plat_priv->dev_lock); + kfree(plat_priv->diag_reg_read_buf); + plat_priv->diag_reg_read_buf = NULL; + + reg_buf = kzalloc(data_len, GFP_KERNEL); + if (!reg_buf) { + mutex_unlock(&plat_priv->dev_lock); + return -ENOMEM; + } + + ret = cnss_wlfw_athdiag_read_send_sync(plat_priv, reg_offset, + mem_type, data_len, + reg_buf); + if (ret) { + kfree(reg_buf); + mutex_unlock(&plat_priv->dev_lock); + return ret; + } + + plat_priv->diag_reg_read_addr = reg_offset; + plat_priv->diag_reg_read_mem_type = mem_type; + plat_priv->diag_reg_read_len = data_len; + plat_priv->diag_reg_read_buf = reg_buf; + mutex_unlock(&plat_priv->dev_lock); + + return count; +} + +static int cnss_reg_read_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, cnss_reg_read_debug_show, inode->i_private); +} + +static const struct file_operations cnss_reg_read_debug_fops = { + .read = seq_read, + .write = cnss_reg_read_debug_write, + .open = cnss_reg_read_debug_open, + .owner = THIS_MODULE, + .llseek = seq_lseek, +}; + +static int cnss_reg_write_debug_show(struct seq_file *s, void *data) +{ + seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs_path>/cnss/reg_write\n"); + + return 0; +} + +static ssize_t cnss_reg_write_debug_write(struct file *fp, + const char __user *user_buf, + size_t count, loff_t *off) +{ + struct cnss_plat_data *plat_priv = + ((struct seq_file *)fp->private_data)->private; + char buf[64]; + char *sptr, *token; + unsigned int len = 0; + u32 reg_offset, mem_type, reg_val; + const char *delim = " "; + int ret = 0; + + if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + cnss_pr_err("Firmware is not ready yet\n"); + return -EINVAL; + } + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + + if (!sptr) + return -EINVAL; + + if (kstrtou32(token, 0, &mem_type)) + return -EINVAL; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + + if (!sptr) + return -EINVAL; + + if (kstrtou32(token, 0, ®_offset)) + return -EINVAL; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + + if (kstrtou32(token, 0, ®_val)) + return -EINVAL; + + ret = cnss_wlfw_athdiag_write_send_sync(plat_priv, reg_offset, mem_type, + sizeof(u32), + (u8 *)®_val); + if (ret) + return ret; + + return count; +} + +static int cnss_reg_write_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, cnss_reg_write_debug_show, inode->i_private); +} + +static const struct file_operations cnss_reg_write_debug_fops = { + .read = seq_read, + .write = cnss_reg_write_debug_write, + .open = cnss_reg_write_debug_open, + .owner = THIS_MODULE, + .llseek = seq_lseek, }; +#ifdef CONFIG_CNSS2_DEBUG +static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv) +{ + struct dentry *root_dentry = plat_priv->root_dentry; + + debugfs_create_file("dev_boot", 0600, root_dentry, plat_priv, + &cnss_dev_boot_debug_fops); + debugfs_create_file("reg_read", 0600, root_dentry, plat_priv, + &cnss_reg_read_debug_fops); + debugfs_create_file("reg_write", 0600, root_dentry, plat_priv, + &cnss_reg_write_debug_fops); + + return 0; +} +#else +static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv) +{ + return 0; +} +#endif + int cnss_debugfs_create(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -139,11 +454,16 @@ int cnss_debugfs_create(struct cnss_plat_data *plat_priv) cnss_pr_err("Unable to create debugfs %d\n", ret); goto out; } + plat_priv->root_dentry = root_dentry; + debugfs_create_file("pin_connect_result", 0644, root_dentry, plat_priv, &cnss_pin_connect_fops); debugfs_create_file("stats", 0644, root_dentry, plat_priv, &cnss_stats_fops); + + cnss_create_debug_only_node(plat_priv); + out: return ret; } diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 29bfe1f4d6ed..23a81ff071ee 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -37,6 +37,7 @@ #define FW_READY_TIMEOUT 20000 #define FW_ASSERT_TIMEOUT 5000 #define CNSS_EVENT_PENDING 2989 +#define WAKE_MSI_NAME "WAKE" static struct cnss_plat_data *plat_env; @@ -56,6 +57,7 @@ MODULE_PARM_DESC(enable_waltest, "Enable to handle firmware waltest"); enum cnss_debug_quirks { LINK_DOWN_SELF_RECOVERY, + SKIP_DEVICE_BOOT, }; unsigned long quirks; @@ -537,6 +539,24 @@ int cnss_set_fw_log_mode(struct device *dev, u8 fw_log_mode) } EXPORT_SYMBOL(cnss_set_fw_log_mode); +u32 cnss_get_wake_msi(struct cnss_plat_data *plat_priv) +{ + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + int ret, num_vectors; + u32 user_base_data, base_vector; + + ret = cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev, + WAKE_MSI_NAME, &num_vectors, + &user_base_data, &base_vector); + + if (ret) { + cnss_pr_err("WAKE MSI is not valid\n"); + return 0; + } + + return user_base_data; +} + static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -573,7 +593,7 @@ static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv) struct cnss_pci_data *pci_priv = plat_priv->bus_priv; if (!plat_priv->driver_ops) { - cnss_pr_err("driver_ops is NULL!"); + cnss_pr_err("driver_ops is NULL\n"); ret = -EINVAL; goto out; } @@ -587,7 +607,7 @@ static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv) goto out; } clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); - } else { + } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) { ret = plat_priv->driver_ops->probe(pci_priv->pci_dev, pci_priv->pci_device_id); if (ret) { @@ -605,6 +625,25 @@ out: return ret; } +static int cnss_driver_call_remove(struct cnss_plat_data *plat_priv) +{ + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + + if (!plat_priv->driver_ops) { + cnss_pr_err("driver_ops is NULL\n"); + return -EINVAL; + } + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { + plat_priv->driver_ops->shutdown(pci_priv->pci_dev); + } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + plat_priv->driver_ops->remove(pci_priv->pci_dev); + clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + + return 0; +} + static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -626,8 +665,11 @@ static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) } else if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) { ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_CALIBRATION_V01); - } else { + } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { ret = cnss_driver_call_probe(plat_priv); + } else { + complete(&plat_priv->power_up_complete); } if (ret) @@ -668,6 +710,10 @@ static char *cnss_driver_event_to_str(enum cnss_driver_event_type type) return "RECOVERY"; case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT: return "FORCE_FW_ASSERT"; + case CNSS_DRIVER_EVENT_POWER_UP: + return "POWER_UP"; + case CNSS_DRIVER_EVENT_POWER_DOWN: + return "POWER_DOWN"; case CNSS_DRIVER_EVENT_MAX: return "EVENT_MAX"; } @@ -746,64 +792,57 @@ out: int cnss_power_up(struct device *dev) { int ret = 0; - void *bus_priv = cnss_bus_dev_to_bus_priv(dev); struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + unsigned int timeout; - if (!bus_priv || !plat_priv) + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); return -ENODEV; - - if (plat_priv->device_id != QCA6174_DEVICE_ID) { - cnss_pr_dbg("Power up is not supported for device ID 0x%lx\n", - plat_priv->device_id); - return 0; } - ret = cnss_power_on_device(plat_priv); - if (ret) { - cnss_pr_err("Failed to power on device, err = %d\n", ret); - goto err_power_on; - } + cnss_pr_dbg("Powering up device\n"); - ret = cnss_resume_pci_link(bus_priv); - if (ret) { - cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); - goto err_resume_link; + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_POWER_UP, + true, NULL); + if (ret) + goto out; + + if (plat_priv->device_id == QCA6174_DEVICE_ID) + goto out; + + timeout = cnss_get_qmi_timeout(); + + reinit_completion(&plat_priv->power_up_complete); + ret = wait_for_completion_timeout(&plat_priv->power_up_complete, + msecs_to_jiffies(timeout) << 2); + if (!ret) { + cnss_pr_err("Timeout waiting for power up to complete\n"); + ret = -EAGAIN; + goto out; } return 0; -err_resume_link: - cnss_power_off_device(plat_priv); -err_power_on: + +out: return ret; } EXPORT_SYMBOL(cnss_power_up); int cnss_power_down(struct device *dev) { - int ret = 0; - void *bus_priv = cnss_bus_dev_to_bus_priv(dev); struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); - if (!bus_priv || !plat_priv) + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); return -ENODEV; - - if (plat_priv->device_id != QCA6174_DEVICE_ID) { - cnss_pr_dbg("Power down is not supported for device ID 0x%lx\n", - plat_priv->device_id); - return 0; } - cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); - cnss_pci_set_monitor_wake_intr(bus_priv, false); - cnss_pci_set_auto_suspended(bus_priv, 0); - - ret = cnss_suspend_pci_link(bus_priv); - if (ret) - cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); + cnss_pr_dbg("Powering down device\n"); - cnss_power_off_device(plat_priv); - - return 0; + return cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_POWER_DOWN, + true, NULL); } EXPORT_SYMBOL(cnss_power_down); @@ -1021,17 +1060,18 @@ static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv) if (!pci_priv) return -ENODEV; + if (test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) + goto skip_driver_remove; + if (!plat_priv->driver_ops) return -EINVAL; - if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { - cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); - plat_priv->driver_ops->remove(pci_priv->pci_dev); - cnss_pci_set_monitor_wake_intr(pci_priv, false); - cnss_pci_set_auto_suspended(pci_priv, 0); - } else { - plat_priv->driver_ops->shutdown(pci_priv->pci_dev); - } + cnss_driver_call_remove(plat_priv); + +skip_driver_remove: + cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); + cnss_pci_set_monitor_wake_intr(pci_priv, false); + cnss_pci_set_auto_suspended(pci_priv, 0); ret = cnss_suspend_pci_link(pci_priv); if (ret) @@ -1039,10 +1079,7 @@ static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv) cnss_power_off_device(plat_priv); - if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { - clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); - clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); - } + clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); return ret; } @@ -1068,7 +1105,8 @@ static int cnss_qca6290_powerup(struct cnss_plat_data *plat_priv) return -ENODEV; } - if (plat_priv->ramdump_info_v2.dump_data_valid) { + if (plat_priv->ramdump_info_v2.dump_data_valid || + test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); cnss_pci_clear_dump_info(pci_priv); } @@ -1128,22 +1166,20 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) return -ENODEV; if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) || - test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state)) + test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) goto skip_driver_remove; if (!plat_priv->driver_ops) return -EINVAL; - if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { - cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); - plat_priv->driver_ops->remove(pci_priv->pci_dev); - cnss_pci_set_monitor_wake_intr(pci_priv, false); - cnss_pci_set_auto_suspended(pci_priv, 0); - } else { - plat_priv->driver_ops->shutdown(pci_priv->pci_dev); - } + cnss_driver_call_remove(plat_priv); skip_driver_remove: + cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); + cnss_pci_set_monitor_wake_intr(pci_priv, false); + cnss_pci_set_auto_suspended(pci_priv, 0); + cnss_pci_stop_mhi(pci_priv); ret = cnss_suspend_pci_link(pci_priv); @@ -1154,11 +1190,7 @@ skip_driver_remove: clear_bit(CNSS_FW_READY, &plat_priv->driver_state); clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); - - if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { - clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); - clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); - } + clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); return ret; } @@ -1661,6 +1693,22 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) return 0; } +static int cnss_power_up_hdlr(struct cnss_plat_data *plat_priv) +{ + struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; + + return cnss_powerup(&subsys_info->subsys_desc); +} + +static int cnss_power_down_hdlr(struct cnss_plat_data *plat_priv) +{ + struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; + + cnss_shutdown(&subsys_info->subsys_desc, false); + + return 0; +} + static void cnss_driver_event_work(struct work_struct *work) { struct cnss_plat_data *plat_priv = @@ -1728,6 +1776,12 @@ static void cnss_driver_event_work(struct work_struct *work) case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT: ret = cnss_force_fw_assert_hdlr(plat_priv); break; + case CNSS_DRIVER_EVENT_POWER_UP: + ret = cnss_power_up_hdlr(plat_priv); + break; + case CNSS_DRIVER_EVENT_POWER_DOWN: + ret = cnss_power_down_hdlr(plat_priv); + break; default: cnss_pr_err("Invalid driver event type: %d", event->type); @@ -2184,13 +2238,15 @@ static int cnss_probe(struct platform_device *plat_dev) if (ret) goto reset_ctx; - ret = cnss_power_on_device(plat_priv); - if (ret) - goto free_res; + if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) { + ret = cnss_power_on_device(plat_priv); + if (ret) + goto free_res; - ret = cnss_pci_init(plat_priv); - if (ret) - goto power_off; + ret = cnss_pci_init(plat_priv); + if (ret) + goto power_off; + } ret = cnss_register_esoc(plat_priv); if (ret) @@ -2226,6 +2282,9 @@ static int cnss_probe(struct platform_device *plat_dev) cnss_pr_err("Failed to init platform device wakeup source, err = %d\n", ret); + init_completion(&plat_priv->power_up_complete); + mutex_init(&plat_priv->dev_lock); + cnss_pr_info("Platform driver probed successfully.\n"); return 0; @@ -2241,9 +2300,11 @@ unreg_bus_scale: unreg_esoc: cnss_unregister_esoc(plat_priv); deinit_pci: - cnss_pci_deinit(plat_priv); + if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) + cnss_pci_deinit(plat_priv); power_off: - cnss_power_off_device(plat_priv); + if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) + cnss_power_off_device(plat_priv); free_res: cnss_put_resources(plat_priv); reset_ctx: @@ -2257,6 +2318,7 @@ static int cnss_remove(struct platform_device *plat_dev) { struct cnss_plat_data *plat_priv = platform_get_drvdata(plat_dev); + complete_all(&plat_priv->power_up_complete); device_init_wakeup(&plat_dev->dev, false); unregister_pm_notifier(&cnss_pm_notifier); del_timer(&plat_priv->fw_boot_timer); diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index e3a8d0cccd52..a5f9ce37b0ea 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -126,6 +126,8 @@ enum cnss_driver_event_type { CNSS_DRIVER_EVENT_UNREGISTER_DRIVER, CNSS_DRIVER_EVENT_RECOVERY, CNSS_DRIVER_EVENT_FORCE_FW_ASSERT, + CNSS_DRIVER_EVENT_POWER_UP, + CNSS_DRIVER_EVENT_POWER_DOWN, CNSS_DRIVER_EVENT_MAX, }; @@ -140,6 +142,7 @@ enum cnss_driver_state { CNSS_DRIVER_RECOVERY, CNSS_FW_BOOT_RECOVERY, CNSS_DEV_ERR_NOTIFY, + CNSS_DRIVER_DEBUG, }; struct cnss_recovery_data { @@ -201,6 +204,12 @@ struct cnss_plat_data { struct dentry *root_dentry; atomic_t pm_count; struct timer_list fw_boot_timer; + struct completion power_up_complete; + struct mutex dev_lock; /* mutex for register access through debugfs */ + u32 diag_reg_read_addr; + u32 diag_reg_read_mem_type; + u32 diag_reg_read_len; + u8 *diag_reg_read_buf; }; void *cnss_bus_dev_to_bus_priv(struct device *dev); @@ -217,5 +226,6 @@ void cnss_unregister_subsys(struct cnss_plat_data *plat_priv); int cnss_register_ramdump(struct cnss_plat_data *plat_priv); void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv); void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv); +u32 cnss_get_wake_msi(struct cnss_plat_data *plat_priv); #endif /* _CNSS_MAIN_H */ diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index a17b72ce03ba..2c297fba5c34 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -787,36 +787,6 @@ int cnss_pci_get_bar_info(struct cnss_pci_data *pci_priv, void __iomem **va, return 0; } -#ifdef CONFIG_CNSS_QCA6290 -#define PCI_MAX_BAR_SIZE 0xD00000 - -static void __iomem *cnss_pci_iomap(struct pci_dev *dev, int bar, - unsigned long maxlen) -{ - resource_size_t start = pci_resource_start(dev, bar); - resource_size_t len = PCI_MAX_BAR_SIZE; - unsigned long flags = pci_resource_flags(dev, bar); - - if (!len || !start) - return NULL; - - if ((flags & IORESOURCE_IO) || (flags & IORESOURCE_MEM)) { - if (flags & IORESOURCE_CACHEABLE && !(flags & IORESOURCE_IO)) - return ioremap(start, len); - else - return ioremap_nocache(start, len); - } - - return NULL; -} -#else -static void __iomem *cnss_pci_iomap(struct pci_dev *dev, int bar, - unsigned long maxlen) -{ - return pci_iomap(dev, bar, maxlen); -} -#endif - static struct cnss_msi_config msi_config = { .total_vectors = 32, .total_users = 4, @@ -1003,7 +973,7 @@ static int cnss_pci_enable_bus(struct cnss_pci_data *pci_priv) pci_set_master(pci_dev); - pci_priv->bar = cnss_pci_iomap(pci_dev, PCI_BAR_NUM, 0); + pci_priv->bar = pci_iomap(pci_dev, PCI_BAR_NUM, 0); if (!pci_priv->bar) { cnss_pr_err("Failed to do PCI IO map!\n"); ret = -EIO; @@ -1366,12 +1336,10 @@ int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv) ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_ON); if (ret) - goto deinit_mhi; + goto out; return 0; -deinit_mhi: - cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); out: return ret; } @@ -1392,8 +1360,12 @@ void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv) cnss_pci_set_mhi_state_bit(pci_priv, CNSS_MHI_RESUME); cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_OFF); - if (!plat_priv->ramdump_info_v2.dump_data_valid) - cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); + + if (plat_priv->ramdump_info_v2.dump_data_valid || + test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) + return; + + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); } static int cnss_pci_probe(struct pci_dev *pci_dev, @@ -1491,6 +1463,11 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, cnss_pci_disable_msi(pci_priv); goto disable_bus; } + ret = cnss_suspend_pci_link(pci_priv); + if (ret) + cnss_pr_err("Failed to suspend PCI link, err = %d\n", + ret); + cnss_power_off_device(plat_priv); break; default: cnss_pr_err("Unknown PCI device found: 0x%x\n", diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index db55d3350eb5..e010e2c39f02 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -39,7 +39,7 @@ static bool daemon_support; module_param(daemon_support, bool, 0600); MODULE_PARM_DESC(daemon_support, "User space has cnss-daemon support or not"); -static bool bdf_bypass = true; +static bool bdf_bypass; #ifdef CONFIG_CNSS2_DEBUG module_param(bdf_bypass, bool, 0600); MODULE_PARM_DESC(bdf_bypass, "If BDF is not found, send dummy BDF to FW"); @@ -50,6 +50,30 @@ enum cnss_bdf_type { CNSS_BDF_ELF, }; +static char *cnss_qmi_mode_to_str(enum wlfw_driver_mode_enum_v01 mode) +{ + switch (mode) { + case QMI_WLFW_MISSION_V01: + return "MISSION"; + case QMI_WLFW_FTM_V01: + return "FTM"; + case QMI_WLFW_EPPING_V01: + return "EPPING"; + case QMI_WLFW_WALTEST_V01: + return "WALTEST"; + case QMI_WLFW_OFF_V01: + return "OFF"; + case QMI_WLFW_CCPM_V01: + return "CCPM"; + case QMI_WLFW_QVIT_V01: + return "QVIT"; + case QMI_WLFW_CALIBRATION_V01: + return "CALIBRATION"; + default: + return "UNKNOWN"; + } +}; + static void cnss_wlfw_clnt_notifier_work(struct work_struct *work) { struct cnss_plat_data *plat_priv = @@ -140,6 +164,12 @@ static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) cnss_pr_dbg("daemon_support is %d\n", req.daemon_support); + req.wake_msi = cnss_get_wake_msi(plat_priv); + if (req.wake_msi) { + cnss_pr_dbg("WAKE MSI base data is %d\n", req.wake_msi); + req.wake_msi_valid = 1; + } + req_desc.max_msg_len = WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_HOST_CAP_REQ_V01; req_desc.ei_array = wlfw_host_cap_req_msg_v01_ei; @@ -579,8 +609,8 @@ int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv, if (!plat_priv) return -ENODEV; - cnss_pr_dbg("Sending mode message, state: 0x%lx, mode: %d\n", - plat_priv->driver_state, mode); + cnss_pr_dbg("Sending mode message, mode: %s(%d), state: 0x%lx\n", + cnss_qmi_mode_to_str(mode), mode, plat_priv->driver_state); if (mode == QMI_WLFW_OFF_V01 && test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { @@ -611,14 +641,15 @@ int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv, cnss_pr_dbg("WLFW service is disconnected while sending mode off request.\n"); return 0; } - cnss_pr_err("Failed to send mode request, mode: %d, err = %d\n", - mode, ret); + cnss_pr_err("Failed to send mode request, mode: %s(%d), err: %d\n", + cnss_qmi_mode_to_str(mode), mode, ret); goto out; } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { - cnss_pr_err("Mode request failed, mode: %d, result: %d err: %d\n", - mode, resp.resp.result, resp.resp.error); + cnss_pr_err("Mode request failed, mode: %s(%d), result: %d, err: %d\n", + cnss_qmi_mode_to_str(mode), mode, resp.resp.result, + resp.resp.error); ret = resp.resp.result; goto out; } diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c index 84a4707e9cc3..7d6a771bc0d5 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c @@ -1097,6 +1097,24 @@ struct elem_info wlfw_cal_report_req_msg_v01_ei[] = { meta_data), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_cal_report_req_msg_v01, + xo_cal_data_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_cal_report_req_msg_v01, + xo_cal_data), + }, + { .data_type = QMI_EOTI, .is_array = NO_ARRAY, .is_array = QMI_COMMON_TLV_TYPE, @@ -1828,6 +1846,24 @@ struct elem_info wlfw_host_cap_req_msg_v01_ei[] = { daemon_support), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_host_cap_req_msg_v01, + wake_msi_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_host_cap_req_msg_v01, + wake_msi), + }, + { .data_type = QMI_EOTI, .is_array = NO_ARRAY, .is_array = QMI_COMMON_TLV_TYPE, @@ -2166,3 +2202,20 @@ struct elem_info wlfw_m3_info_resp_msg_v01_ei[] = { .is_array = QMI_COMMON_TLV_TYPE, }, }; + +struct elem_info wlfw_xo_cal_ind_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_xo_cal_ind_msg_v01, + xo_cal_data), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h index cb8225a7c3c3..a3081433cc2b 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h @@ -31,6 +31,7 @@ #define QMI_WLFW_M3_INFO_RESP_V01 0x003C #define QMI_WLFW_CAL_UPDATE_RESP_V01 0x0029 #define QMI_WLFW_CAL_DOWNLOAD_RESP_V01 0x0027 +#define QMI_WLFW_XO_CAL_IND_V01 0x003D #define QMI_WLFW_INI_RESP_V01 0x002F #define QMI_WLFW_CAL_REPORT_RESP_V01 0x0026 #define QMI_WLFW_MAC_ADDR_RESP_V01 0x0033 @@ -339,9 +340,11 @@ extern struct elem_info wlfw_bdf_download_resp_msg_v01_ei[]; struct wlfw_cal_report_req_msg_v01 { u32 meta_data_len; enum wlfw_cal_temp_id_enum_v01 meta_data[QMI_WLFW_MAX_NUM_CAL_V01]; + u8 xo_cal_data_valid; + u8 xo_cal_data; }; -#define WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN 24 +#define WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN 28 extern struct elem_info wlfw_cal_report_req_msg_v01_ei[]; struct wlfw_cal_report_resp_msg_v01 { @@ -532,9 +535,11 @@ extern struct elem_info wlfw_mac_addr_resp_msg_v01_ei[]; struct wlfw_host_cap_req_msg_v01 { u8 daemon_support_valid; u8 daemon_support; + u8 wake_msi_valid; + u32 wake_msi; }; -#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 4 +#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 11 extern struct elem_info wlfw_host_cap_req_msg_v01_ei[]; struct wlfw_host_cap_resp_msg_v01 { @@ -642,4 +647,11 @@ struct wlfw_m3_info_resp_msg_v01 { #define WLFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 extern struct elem_info wlfw_m3_info_resp_msg_v01_ei[]; +struct wlfw_xo_cal_ind_msg_v01 { + u8 xo_cal_data; +}; + +#define WLFW_XO_CAL_IND_MSG_V01_MAX_MSG_LEN 4 +extern struct elem_info wlfw_xo_cal_ind_msg_v01_ei[]; + #endif diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index d59769e858f4..019d7165a045 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2539,7 +2539,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, tasklet_hrtimer_init(&data->beacon_timer, mac80211_hwsim_beacon, - CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); + CLOCK_MONOTONIC, HRTIMER_MODE_ABS); spin_lock_bh(&hwsim_radio_lock); list_add_tail(&data->list, &hwsim_radios); diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 44f059f7f34e..9ebe00ea8f81 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -71,7 +71,7 @@ * only support SPI for 12xx - this code should be reworked when 18xx * support is introduced */ -#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) +#define SPI_AGGR_BUFFER_SIZE (4 * SZ_4K) /* Maximum number of SPI write chunks */ #define WSPI_MAX_NUM_OF_CHUNKS \ diff --git a/drivers/net/wireless/wcnss/wcnss_vreg.c b/drivers/net/wireless/wcnss/wcnss_vreg.c index 82b90ad00f8b..d94bd90f64da 100644 --- a/drivers/net/wireless/wcnss/wcnss_vreg.c +++ b/drivers/net/wireless/wcnss/wcnss_vreg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2015, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -71,23 +71,33 @@ static int is_power_on; struct vregs_info { const char * const name; + const char * const curr; + const char * const volt; int state; + bool required; struct regulator *regulator; }; /* IRIS regulators for Pronto hardware */ -static struct vregs_info iris_vregs_pronto[] = { - {"qcom,iris-vddxo", VREG_NULL_CONFIG, NULL}, - {"qcom,iris-vddrfa", VREG_NULL_CONFIG, NULL}, - {"qcom,iris-vddpa", VREG_NULL_CONFIG, NULL}, - {"qcom,iris-vdddig", VREG_NULL_CONFIG, NULL}, +static struct vregs_info iris_vregs[] = { + {"qcom,iris-vddxo", "qcom,iris-vddxo-current", + "qcom,iris-vddxo-voltage-level", VREG_NULL_CONFIG, true, NULL}, + {"qcom,iris-vddrfa", "qcom,iris-vddrfa-current", + "qcom,iris-vddrfa-voltage-level", VREG_NULL_CONFIG, true, NULL}, + {"qcom,iris-vddpa", "qcom,iris-vddpa-current", + "qcom,iris-vddpa-voltage-level", VREG_NULL_CONFIG, false, NULL}, + {"qcom,iris-vdddig", "qcom,iris-vdddig-current", + "qcom,iris-vdddig-voltage-level", VREG_NULL_CONFIG, true, NULL}, }; /* WCNSS regulators for Pronto hardware */ static struct vregs_info pronto_vregs[] = { - {"qcom,pronto-vddmx", VREG_NULL_CONFIG, NULL}, - {"qcom,pronto-vddcx", VREG_NULL_CONFIG, NULL}, - {"qcom,pronto-vddpx", VREG_NULL_CONFIG, NULL}, + {"qcom,pronto-vddmx", "qcom,pronto-vddmx-current", + "qcom,vddmx-voltage-level", VREG_NULL_CONFIG, true, NULL}, + {"qcom,pronto-vddcx", "qcom,pronto-vddcx-current", + "qcom,vddcx-voltage-level", VREG_NULL_CONFIG, true, NULL}, + {"qcom,pronto-vddpx", "qcom,pronto-vddpx-current", + "qcom,vddpx-voltage-level", VREG_NULL_CONFIG, true, NULL}, }; struct host_driver { @@ -184,6 +194,129 @@ int validate_iris_chip_id(u32 reg) } } +static void wcnss_free_regulator(void) +{ + int vreg_i; + + /* Free pronto voltage regulators from device node */ + for (vreg_i = 0; vreg_i < PRONTO_REGULATORS; vreg_i++) { + if (pronto_vregs[vreg_i].state) { + regulator_put(pronto_vregs[vreg_i].regulator); + pronto_vregs[vreg_i].state = VREG_NULL_CONFIG; + } + } + + /* Free IRIS voltage regulators from device node */ + for (vreg_i = 0; vreg_i < IRIS_REGULATORS; vreg_i++) { + if (iris_vregs[vreg_i].state) { + regulator_put(iris_vregs[vreg_i].regulator); + iris_vregs[vreg_i].state = VREG_NULL_CONFIG; + } + } +} + +static int +wcnss_dt_parse_vreg_level(struct device *dev, int index, + const char *current_vreg_name, const char *vreg_name, + struct vregs_level *vlevel) +{ + int ret = 0; + /* array used to store nominal, low and high voltage values */ + u32 voltage_levels[3], current_vreg; + + ret = of_property_read_u32_array(dev->of_node, vreg_name, + voltage_levels, + ARRAY_SIZE(voltage_levels)); + if (ret) { + dev_err(dev, "error reading %s property\n", vreg_name); + return ret; + } + + vlevel[index].nominal_min = voltage_levels[0]; + vlevel[index].low_power_min = voltage_levels[1]; + vlevel[index].max_voltage = voltage_levels[2]; + + ret = of_property_read_u32(dev->of_node, current_vreg_name, + ¤t_vreg); + if (ret) { + dev_err(dev, "error reading %s property\n", current_vreg_name); + return ret; + } + + vlevel[index].uA_load = current_vreg; + + return ret; +} + +int +wcnss_parse_voltage_regulator(struct wcnss_wlan_config *wlan_config, + struct device *dev) +{ + int rc, vreg_i; + + /* Parse pronto voltage regulators from device node */ + for (vreg_i = 0; vreg_i < PRONTO_REGULATORS; vreg_i++) { + pronto_vregs[vreg_i].regulator = + regulator_get(dev, pronto_vregs[vreg_i].name); + if (IS_ERR(pronto_vregs[vreg_i].regulator)) { + if (pronto_vregs[vreg_i].required) { + rc = PTR_ERR(pronto_vregs[vreg_i].regulator); + dev_err(dev, "regulator get of %s failed (%d)\n", + pronto_vregs[vreg_i].name, rc); + goto wcnss_vreg_get_err; + } else { + dev_dbg(dev, "Skip optional regulator configuration: %s\n", + pronto_vregs[vreg_i].name); + continue; + } + } + + pronto_vregs[vreg_i].state |= VREG_GET_REGULATOR_MASK; + rc = wcnss_dt_parse_vreg_level(dev, vreg_i, + pronto_vregs[vreg_i].curr, + pronto_vregs[vreg_i].volt, + wlan_config->pronto_vlevel); + if (rc) { + dev_err(dev, "error reading voltage-level property\n"); + goto wcnss_vreg_get_err; + } + } + + /* Parse iris voltage regulators from device node */ + for (vreg_i = 0; vreg_i < IRIS_REGULATORS; vreg_i++) { + iris_vregs[vreg_i].regulator = + regulator_get(dev, iris_vregs[vreg_i].name); + if (IS_ERR(iris_vregs[vreg_i].regulator)) { + if (iris_vregs[vreg_i].required) { + rc = PTR_ERR(iris_vregs[vreg_i].regulator); + dev_err(dev, "regulator get of %s failed (%d)\n", + iris_vregs[vreg_i].name, rc); + goto wcnss_vreg_get_err; + } else { + dev_dbg(dev, "Skip optional regulator configuration: %s\n", + iris_vregs[vreg_i].name); + continue; + } + } + + iris_vregs[vreg_i].state |= VREG_GET_REGULATOR_MASK; + rc = wcnss_dt_parse_vreg_level(dev, vreg_i, + iris_vregs[vreg_i].curr, + iris_vregs[vreg_i].volt, + wlan_config->iris_vlevel); + if (rc) { + dev_err(dev, "error reading voltage-level property\n"); + goto wcnss_vreg_get_err; + } + } + + return 0; + +wcnss_vreg_get_err: + wcnss_free_regulator(); + return rc; +} + void wcnss_iris_reset(u32 reg, void __iomem *pmu_conf_reg) { /* Reset IRIS */ @@ -419,11 +552,11 @@ static void wcnss_vregs_off(struct vregs_info regulators[], uint size, /* Remove PWM mode */ if (regulators[i].state & VREG_OPTIMUM_MODE_MASK) { - rc = regulator_set_optimum_mode( - regulators[i].regulator, 0); - if (rc < 0) - pr_err("regulator_set_optimum_mode(%s) failed (%d)\n", - regulators[i].name, rc); + rc = regulator_set_load(regulators[i].regulator, 0); + if (rc < 0) { + pr_err("regulator set load(%s) failed (%d)\n", + regulators[i].name, rc); + } } /* Set voltage to lowest level */ @@ -478,18 +611,10 @@ static int wcnss_vregs_on(struct device *dev, } for (i = 0; i < size; i++) { - /* Get regulator source */ - regulators[i].regulator = - regulator_get(dev, regulators[i].name); - if (IS_ERR(regulators[i].regulator)) { - rc = PTR_ERR(regulators[i].regulator); - pr_err("regulator get of %s failed (%d)\n", - regulators[i].name, rc); - goto fail; - } - regulators[i].state |= VREG_GET_REGULATOR_MASK; - reg_cnt = regulator_count_voltages(regulators[i].regulator); + if (regulators[i].state == VREG_NULL_CONFIG) + continue; + reg_cnt = regulator_count_voltages(regulators[i].regulator); /* Set voltage to nominal. Exclude swtiches e.g. LVS */ if ((voltage_level[i].nominal_min || voltage_level[i].max_voltage) && (reg_cnt > 0)) { @@ -518,11 +643,11 @@ static int wcnss_vregs_on(struct device *dev, /* Vote for PWM/PFM mode if needed */ if (voltage_level[i].uA_load && (reg_cnt > 0)) { - rc = regulator_set_optimum_mode(regulators[i].regulator, - voltage_level[i].uA_load); + rc = regulator_set_load(regulators[i].regulator, + voltage_level[i].uA_load); if (rc < 0) { - pr_err("regulator_set_optimum_mode(%s) failed (%d)\n", - regulators[i].name, rc); + pr_err("regulator set load(%s) failed (%d)\n", + regulators[i].name, rc); goto fail; } regulators[i].state |= VREG_OPTIMUM_MODE_MASK; @@ -551,8 +676,7 @@ static void wcnss_iris_vregs_off(enum wcnss_hw_type hw_type, { switch (hw_type) { case WCNSS_PRONTO_HW: - wcnss_vregs_off(iris_vregs_pronto, - ARRAY_SIZE(iris_vregs_pronto), + wcnss_vregs_off(iris_vregs, ARRAY_SIZE(iris_vregs), cfg->iris_vlevel); break; default: @@ -569,8 +693,7 @@ static int wcnss_iris_vregs_on(struct device *dev, switch (hw_type) { case WCNSS_PRONTO_HW: - ret = wcnss_vregs_on(dev, iris_vregs_pronto, - ARRAY_SIZE(iris_vregs_pronto), + ret = wcnss_vregs_on(dev, iris_vregs, ARRAY_SIZE(iris_vregs), cfg->iris_vlevel); break; default: diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c index 505a9e016777..b604f61e1a05 100644 --- a/drivers/net/wireless/wcnss/wcnss_wlan.c +++ b/drivers/net/wireless/wcnss/wcnss_wlan.c @@ -36,6 +36,8 @@ #include <linux/qpnp/qpnp-adc.h> #include <linux/pinctrl/consumer.h> #include <linux/pm_qos.h> +#include <linux/bitops.h> +#include <soc/qcom/socinfo.h> #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/subsystem_notif.h> @@ -56,11 +58,27 @@ #define WCNSS_PM_QOS_TIMEOUT 15000 #define IS_CAL_DATA_PRESENT 0 #define WAIT_FOR_CBC_IND 2 +#define WCNSS_DUAL_BAND_CAPABILITY_OFFSET BIT(8) /* module params */ #define WCNSS_CONFIG_UNSPECIFIED (-1) #define UINT32_MAX (0xFFFFFFFFU) +#define SUBSYS_NOTIF_MIN_INDEX 0 +#define SUBSYS_NOTIF_MAX_INDEX 9 +char *wcnss_subsys_notif_type[] = { + "SUBSYS_BEFORE_SHUTDOWN", + "SUBSYS_AFTER_SHUTDOWN", + "SUBSYS_BEFORE_POWERUP", + "SUBSYS_AFTER_POWERUP", + "SUBSYS_RAMDUMP_NOTIFICATION", + "SUBSYS_POWERUP_FAILURE", + "SUBSYS_PROXY_VOTE", + "SUBSYS_PROXY_UNVOTE", + "SUBSYS_SOC_RESET", + "SUBSYS_NOTIF_TYPE_COUNT" +}; + static int has_48mhz_xo = WCNSS_CONFIG_UNSPECIFIED; module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present"); @@ -119,6 +137,8 @@ static DEFINE_SPINLOCK(reg_spinlock); #define PRONTO_PMU_COM_CSR_OFFSET 0x1040 #define PRONTO_PMU_SOFT_RESET_OFFSET 0x104C +#define PRONTO_QFUSE_DUAL_BAND_OFFSET 0x0018 + #define A2XB_CFG_OFFSET 0x00 #define A2XB_INT_SRC_OFFSET 0x0c #define A2XB_TSTBUS_CTRL_OFFSET 0x14 @@ -183,11 +203,9 @@ static DEFINE_SPINLOCK(reg_spinlock); #define WCNSS_MAX_BUILD_VER_LEN 256 #define WCNSS_MAX_CMD_LEN (128) #define WCNSS_MIN_CMD_LEN (3) -#define WCNSS_MIN_SERIAL_LEN (6) /* control messages from userspace */ #define WCNSS_USR_CTRL_MSG_START 0x00000000 -#define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1) #define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2) #define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3) @@ -381,6 +399,7 @@ static struct { void __iomem *pronto_saw2_base; void __iomem *pronto_pll_base; void __iomem *pronto_mcu_base; + void __iomem *pronto_qfuse; void __iomem *wlan_tx_status; void __iomem *wlan_tx_phy_aborts; void __iomem *wlan_brdg_err_source; @@ -397,7 +416,7 @@ static struct { int user_cal_read; int user_cal_available; u32 user_cal_rcvd; - int user_cal_exp_size; + u32 user_cal_exp_size; int iris_xo_mode_set; int fw_vbatt_state; char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE]; @@ -423,6 +442,9 @@ static struct { int pc_disabled; struct delayed_work wcnss_pm_qos_del_req; struct mutex pm_qos_mutex; + struct clk *snoc_wcnss; + unsigned int snoc_wcnss_clock_freq; + bool is_dual_band_disabled; } *penv = NULL; static ssize_t wcnss_wlan_macaddr_store(struct device *dev, @@ -474,34 +496,6 @@ static ssize_t wcnss_wlan_macaddr_show(struct device *dev, static DEVICE_ATTR(wcnss_mac_addr, S_IRUSR | S_IWUSR, wcnss_wlan_macaddr_show, wcnss_wlan_macaddr_store); -static ssize_t wcnss_serial_number_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - if (!penv) - return -ENODEV; - - return scnprintf(buf, PAGE_SIZE, "%08X\n", penv->serial_number); -} - -static ssize_t wcnss_serial_number_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned int value; - - if (!penv) - return -ENODEV; - - if (sscanf(buf, "%08X", &value) != 1) - return -EINVAL; - - penv->serial_number = value; - return count; -} - -static DEVICE_ATTR(serial_number, S_IRUSR | S_IWUSR, - wcnss_serial_number_show, wcnss_serial_number_store); - - static ssize_t wcnss_thermal_mitigation_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -595,7 +589,31 @@ void wcnss_pronto_is_a2xb_bus_stall(void *tst_addr, u32 fifo_mask, char *type) } } -/* Log pronto debug registers before sending reset interrupt */ +int wcnss_get_dual_band_capability_info(struct platform_device *pdev) +{ + u32 reg = 0; + struct resource *res; + + res = platform_get_resource_byname( + pdev, IORESOURCE_MEM, "pronto_qfuse"); + if (!res) + return -EINVAL; + + penv->pronto_qfuse = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(penv->pronto_qfuse)) + return -ENOMEM; + + reg = readl_relaxed(penv->pronto_qfuse + + PRONTO_QFUSE_DUAL_BAND_OFFSET); + if (reg & WCNSS_DUAL_BAND_CAPABILITY_OFFSET) + penv->is_dual_band_disabled = true; + else + penv->is_dual_band_disabled = false; + + return 0; +} + +/* Log pronto debug registers during SSR Timeout CB */ void wcnss_pronto_log_debug_regs(void) { void __iomem *reg_addr, *tst_addr, *tst_ctrl_addr; @@ -1146,13 +1164,9 @@ static int wcnss_create_sysfs(struct device *dev) if (!dev) return -ENODEV; - ret = device_create_file(dev, &dev_attr_serial_number); - if (ret) - return ret; - ret = device_create_file(dev, &dev_attr_thermal_mitigation); if (ret) - goto remove_serial; + return ret; ret = device_create_file(dev, &dev_attr_wcnss_version); if (ret) @@ -1168,8 +1182,6 @@ remove_version: device_remove_file(dev, &dev_attr_wcnss_version); remove_thermal: device_remove_file(dev, &dev_attr_thermal_mitigation); -remove_serial: - device_remove_file(dev, &dev_attr_serial_number); return ret; } @@ -1177,7 +1189,6 @@ remove_serial: static void wcnss_remove_sysfs(struct device *dev) { if (dev) { - device_remove_file(dev, &dev_attr_serial_number); device_remove_file(dev, &dev_attr_thermal_mitigation); device_remove_file(dev, &dev_attr_wcnss_version); device_remove_file(dev, &dev_attr_wcnss_mac_addr); @@ -1625,8 +1636,13 @@ EXPORT_SYMBOL(wcnss_unregister_thermal_mitigation); unsigned int wcnss_get_serial_number(void) { - if (penv) + if (penv) { + penv->serial_number = socinfo_get_serial_number(); + pr_info("%s: Device serial number: %u\n", + __func__, penv->serial_number); return penv->serial_number; + } + return 0; } EXPORT_SYMBOL(wcnss_get_serial_number); @@ -1683,6 +1699,14 @@ int wcnss_wlan_iris_xo_mode(void) } EXPORT_SYMBOL(wcnss_wlan_iris_xo_mode); +int wcnss_wlan_dual_band_disabled(void) +{ + if (penv && penv->pdev) + return penv->is_dual_band_disabled; + + return -EINVAL; +} +EXPORT_SYMBOL(wcnss_wlan_dual_band_disabled); void wcnss_suspend_notify(void) { @@ -2608,15 +2632,6 @@ void process_usr_ctrl_cmd(u8 *buf, size_t len) switch (cmd) { - case WCNSS_USR_SERIAL_NUM: - if (WCNSS_MIN_SERIAL_LEN > len) { - pr_err("%s: Invalid serial number\n", __func__); - return; - } - penv->serial_number = buf[2] << 24 | buf[3] << 16 - | buf[4] << 8 | buf[5]; - break; - case WCNSS_USR_HAS_CAL_DATA: if (1 < buf[2]) pr_err("%s: Invalid data for cal %d\n", __func__, @@ -2674,145 +2689,34 @@ static struct miscdevice wcnss_usr_ctrl = { }; static int -wcnss_dt_parse_vreg_level(struct device *dev, int index, - const char *current_vreg_name, const char *vreg_name, - struct vregs_level *vlevel) -{ - int ret = 0; - /* array used to store nominal, low and high voltage values - */ - u32 voltage_levels[3], current_vreg; - - ret = of_property_read_u32_array(dev->of_node, vreg_name, - voltage_levels, - ARRAY_SIZE(voltage_levels)); - if (ret) { - dev_err(dev, "error reading %s property\n", vreg_name); - return ret; - } - - vlevel[index].nominal_min = voltage_levels[0]; - vlevel[index].low_power_min = voltage_levels[1]; - vlevel[index].max_voltage = voltage_levels[2]; - - ret = of_property_read_u32(dev->of_node, current_vreg_name, - ¤t_vreg); - if (ret) { - dev_err(dev, "error reading %s property\n", current_vreg_name); - return ret; - } - - vlevel[index].uA_load = current_vreg; - - return ret; -} - -static int wcnss_trigger_config(struct platform_device *pdev) { int ret; - int rc, index = 0; + int rc; struct qcom_wcnss_opts *pdata; struct resource *res; int is_pronto_vadc; int is_pronto_v3; int pil_retry = 0; - int has_pronto_hw = of_property_read_bool(pdev->dev.of_node, - "qcom,has-pronto-hw"); + struct device_node *node = (&pdev->dev)->of_node; + int has_pronto_hw = of_property_read_bool(node, "qcom,has-pronto-hw"); - is_pronto_vadc = of_property_read_bool(pdev->dev.of_node, - "qcom,is-pronto-vadc"); + is_pronto_vadc = of_property_read_bool(node, "qcom,is-pronto-vadc"); + is_pronto_v3 = of_property_read_bool(node, "qcom,is-pronto-v3"); - is_pronto_v3 = of_property_read_bool(pdev->dev.of_node, - "qcom,is-pronto-v3"); + penv->is_vsys_adc_channel = + of_property_read_bool(node, "qcom,has-vsys-adc-channel"); + penv->is_a2xb_split_reg = + of_property_read_bool(node, "qcom,has-a2xb-split-reg"); - penv->is_vsys_adc_channel = of_property_read_bool(pdev->dev.of_node, - "qcom,has-vsys-adc-channel"); - - penv->is_a2xb_split_reg = of_property_read_bool(pdev->dev.of_node, - "qcom,has-a2xb-split-reg"); - - if (of_property_read_u32(pdev->dev.of_node, - "qcom,wlan-rx-buff-count", &penv->wlan_rx_buff_count)) { + if (of_property_read_u32(node, "qcom,wlan-rx-buff-count", + &penv->wlan_rx_buff_count)) { penv->wlan_rx_buff_count = WCNSS_DEF_WLAN_RX_BUFF_COUNT; } - ret = wcnss_dt_parse_vreg_level(&pdev->dev, index, - "qcom,pronto-vddmx-current", - "qcom,vddmx-voltage-level", - penv->wlan_config.pronto_vlevel); - - if (ret) { - dev_err(&pdev->dev, "error reading voltage-level property\n"); - goto fail; - } - - index++; - ret = wcnss_dt_parse_vreg_level(&pdev->dev, index, - "qcom,pronto-vddcx-current", - "qcom,vddcx-voltage-level", - penv->wlan_config.pronto_vlevel); - - if (ret) { - dev_err(&pdev->dev, "error reading voltage-level property\n"); - goto fail; - } - - index++; - ret = wcnss_dt_parse_vreg_level(&pdev->dev, index, - "qcom,pronto-vddpx-current", - "qcom,vddpx-voltage-level", - penv->wlan_config.pronto_vlevel); - - if (ret) { - dev_err(&pdev->dev, "error reading voltage-level property\n"); - goto fail; - } - - /* assign 0 to index now onwards, index variable re used to - * represent iris regulator index - */ - index = 0; - ret = wcnss_dt_parse_vreg_level(&pdev->dev, index, - "qcom,iris-vddxo-current", - "qcom,iris-vddxo-voltage-level", - penv->wlan_config.iris_vlevel); - - if (ret) { - dev_err(&pdev->dev, "error reading voltage-level property\n"); - goto fail; - } - - index++; - ret = wcnss_dt_parse_vreg_level(&pdev->dev, index, - "qcom,iris-vddrfa-current", - "qcom,iris-vddrfa-voltage-level", - penv->wlan_config.iris_vlevel); - - if (ret) { - dev_err(&pdev->dev, "error reading voltage-level property\n"); - goto fail; - } - - index++; - ret = wcnss_dt_parse_vreg_level(&pdev->dev, index, - "qcom,iris-vddpa-current", - "qcom,iris-vddpa-voltage-level", - penv->wlan_config.iris_vlevel); - - if (ret) { - dev_err(&pdev->dev, "error reading voltage-level property\n"); - goto fail; - } - - index++; - ret = wcnss_dt_parse_vreg_level(&pdev->dev, index, - "qcom,iris-vdddig-current", - "qcom,iris-vdddig-voltage-level", - penv->wlan_config.iris_vlevel); - - if (ret) { - dev_err(&pdev->dev, "error reading voltage-level property\n"); + rc = wcnss_parse_voltage_regulator(&penv->wlan_config, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "Failed to parse voltage regulators\n"); goto fail; } @@ -2825,8 +2729,8 @@ wcnss_trigger_config(struct platform_device *pdev) pdata = pdev->dev.platform_data; if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo) { if (has_pronto_hw) { - has_48mhz_xo = of_property_read_bool(pdev->dev.of_node, - "qcom,has-48mhz-xo"); + has_48mhz_xo = + of_property_read_bool(node, "qcom,has-48mhz-xo"); } else { has_48mhz_xo = pdata->has_48mhz_xo; } @@ -2837,8 +2741,8 @@ wcnss_trigger_config(struct platform_device *pdev) penv->wlan_config.is_pronto_v3 = is_pronto_v3; if (WCNSS_CONFIG_UNSPECIFIED == has_autodetect_xo && has_pronto_hw) { - has_autodetect_xo = of_property_read_bool(pdev->dev.of_node, - "qcom,has-autodetect-xo"); + has_autodetect_xo = + of_property_read_bool(node, "qcom,has-autodetect-xo"); } penv->thermal_mitigation = 0; @@ -3118,6 +3022,16 @@ wcnss_trigger_config(struct platform_device *pdev) __func__); goto fail_ioremap2; } + + if (of_property_read_bool(node, + "qcom,is-dual-band-disabled")) { + ret = wcnss_get_dual_band_capability_info(pdev); + if (ret) { + pr_err( + "%s: failed to get dual band info\n", __func__); + goto fail_ioremap2; + } + } } penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss"); @@ -3129,11 +3043,26 @@ wcnss_trigger_config(struct platform_device *pdev) penv->fw_vbatt_state = WCNSS_CONFIG_UNSPECIFIED; } + penv->snoc_wcnss = devm_clk_get(&penv->pdev->dev, "snoc_wcnss"); + if (IS_ERR(penv->snoc_wcnss)) { + pr_err("%s: couldn't get snoc_wcnss\n", __func__); + penv->snoc_wcnss = NULL; + } else { + if (of_property_read_u32(pdev->dev.of_node, + "qcom,snoc-wcnss-clock-freq", + &penv->snoc_wcnss_clock_freq)) { + pr_debug("%s: wcnss snoc clock frequency is not defined\n", + __func__); + devm_clk_put(&penv->pdev->dev, penv->snoc_wcnss); + penv->snoc_wcnss = NULL; + } + } + if (penv->wlan_config.is_pronto_vadc) { penv->vadc_dev = qpnp_get_vadc(&penv->pdev->dev, "wcnss"); if (IS_ERR(penv->vadc_dev)) { - pr_err("%s: vadc get failed\n", __func__); + pr_debug("%s: vadc get failed\n", __func__); penv->vadc_dev = NULL; } else { rc = wcnss_get_battery_volt(&penv->wlan_config.vbatt); @@ -3191,6 +3120,38 @@ fail: return ret; } +/* Driver requires to directly vote the snoc clocks + * To enable and disable snoc clock, it call + * wcnss_snoc_vote function + */ +void wcnss_snoc_vote(bool clk_chk_en) +{ + int rc; + + if (!penv->snoc_wcnss) { + pr_err("%s: couldn't get clk snoc_wcnss\n", __func__); + return; + } + + if (clk_chk_en) { + rc = clk_set_rate(penv->snoc_wcnss, + penv->snoc_wcnss_clock_freq); + if (rc) { + pr_err("%s: snoc_wcnss_clk-clk_set_rate failed =%d\n", + __func__, rc); + return; + } + + if (clk_prepare_enable(penv->snoc_wcnss)) { + pr_err("%s: snoc_wcnss clk enable failed\n", __func__); + return; + } + } else { + clk_disable_unprepare(penv->snoc_wcnss); + } +} +EXPORT_SYMBOL(wcnss_snoc_vote); + /* wlan prop driver cannot invoke cancel_work_sync * function directly, so to invoke this function it * call wcnss_flush_work function @@ -3377,7 +3338,15 @@ static int wcnss_notif_cb(struct notifier_block *this, unsigned long code, struct notif_data *data = (struct notif_data *)ss_handle; int ret, xo_mode; - pr_info("%s: wcnss notification event: %lu\n", __func__, code); + if (!(code >= SUBSYS_NOTIF_MIN_INDEX) && + (code <= SUBSYS_NOTIF_MAX_INDEX)) { + pr_debug("%s: Invaild subsystem notification code: %lu\n", + __func__, code); + return NOTIFY_DONE; + } + + pr_debug("%s: wcnss notification event: %lu : %s\n", + __func__, code, wcnss_subsys_notif_type[code]); if (code == SUBSYS_PROXY_VOTE) { if (pdev && pwlanconfig) { diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c index 532db28145c7..a5d7332dfce5 100644 --- a/drivers/nfc/fdp/i2c.c +++ b/drivers/nfc/fdp/i2c.c @@ -210,14 +210,14 @@ static irqreturn_t fdp_nci_i2c_irq_thread_fn(int irq, void *phy_id) struct sk_buff *skb; int r; - client = phy->i2c_dev; - dev_dbg(&client->dev, "%s\n", __func__); - if (!phy || irq != phy->i2c_dev->irq) { WARN_ON_ONCE(1); return IRQ_NONE; } + client = phy->i2c_dev; + dev_dbg(&client->dev, "%s\n", __func__); + r = fdp_nci_i2c_read(phy, &skb); if (r == -EREMOTEIO) diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c index f8dcdf4b24f6..af62c4c854f3 100644 --- a/drivers/nfc/nfcmrvl/fw_dnld.c +++ b/drivers/nfc/nfcmrvl/fw_dnld.c @@ -459,7 +459,7 @@ int nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv) INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work); snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq", - dev_name(priv->dev)); + dev_name(&priv->ndev->nfc_dev->dev)); priv->fw_dnld.rx_wq = create_singlethread_workqueue(name); if (!priv->fw_dnld.rx_wq) return -ENOMEM; @@ -496,6 +496,7 @@ int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name) { struct nfcmrvl_private *priv = nci_get_drvdata(ndev); struct nfcmrvl_fw_dnld *fw_dnld = &priv->fw_dnld; + int res; if (!priv->support_fw_dnld) return -ENOTSUPP; @@ -511,7 +512,9 @@ int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name) */ /* Retrieve FW binary */ - if (request_firmware(&fw_dnld->fw, firmware_name, priv->dev) < 0) { + res = request_firmware(&fw_dnld->fw, firmware_name, + &ndev->nfc_dev->dev); + if (res < 0) { nfc_err(priv->dev, "failed to retrieve FW %s", firmware_name); return -ENOENT; } diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 51c8240a1672..a446590a71ca 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -124,12 +124,13 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy, memcpy(&priv->config, pdata, sizeof(*pdata)); if (priv->config.reset_n_io) { - rc = devm_gpio_request_one(dev, - priv->config.reset_n_io, - GPIOF_OUT_INIT_LOW, - "nfcmrvl_reset_n"); - if (rc < 0) + rc = gpio_request_one(priv->config.reset_n_io, + GPIOF_OUT_INIT_LOW, + "nfcmrvl_reset_n"); + if (rc < 0) { + priv->config.reset_n_io = 0; nfc_err(dev, "failed to request reset_n io\n"); + } } if (phy == NFCMRVL_PHY_SPI) { @@ -154,7 +155,13 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy, if (!priv->ndev) { nfc_err(dev, "nci_allocate_device failed\n"); rc = -ENOMEM; - goto error; + goto error_free_gpio; + } + + rc = nfcmrvl_fw_dnld_init(priv); + if (rc) { + nfc_err(dev, "failed to initialize FW download %d\n", rc); + goto error_free_dev; } nci_set_drvdata(priv->ndev, priv); @@ -162,24 +169,22 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy, rc = nci_register_device(priv->ndev); if (rc) { nfc_err(dev, "nci_register_device failed %d\n", rc); - goto error_free_dev; + goto error_fw_dnld_deinit; } /* Ensure that controller is powered off */ nfcmrvl_chip_halt(priv); - rc = nfcmrvl_fw_dnld_init(priv); - if (rc) { - nfc_err(dev, "failed to initialize FW download %d\n", rc); - goto error_free_dev; - } - nfc_info(dev, "registered with nci successfully\n"); return priv; +error_fw_dnld_deinit: + nfcmrvl_fw_dnld_deinit(priv); error_free_dev: nci_free_device(priv->ndev); -error: +error_free_gpio: + if (priv->config.reset_n_io) + gpio_free(priv->config.reset_n_io); kfree(priv); return ERR_PTR(rc); } @@ -195,7 +200,7 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv) nfcmrvl_fw_dnld_deinit(priv); if (priv->config.reset_n_io) - devm_gpio_free(priv->dev, priv->config.reset_n_io); + gpio_free(priv->config.reset_n_io); nci_unregister_device(ndev); nci_free_device(ndev); diff --git a/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c index 83a99e38e7bd..6c0c301611c4 100644 --- a/drivers/nfc/nfcmrvl/uart.c +++ b/drivers/nfc/nfcmrvl/uart.c @@ -109,6 +109,7 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu) struct nfcmrvl_private *priv; struct nfcmrvl_platform_data *pdata = NULL; struct nfcmrvl_platform_data config; + struct device *dev = nu->tty->dev; /* * Platform data cannot be used here since usually it is already used @@ -116,9 +117,8 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu) * and check if DT entries were added. */ - if (nu->tty->dev->parent && nu->tty->dev->parent->of_node) - if (nfcmrvl_uart_parse_dt(nu->tty->dev->parent->of_node, - &config) == 0) + if (dev && dev->parent && dev->parent->of_node) + if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config) == 0) pdata = &config; if (!pdata) { @@ -131,7 +131,7 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu) } priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_UART, nu, &uart_ops, - nu->tty->dev, pdata); + dev, pdata); if (IS_ERR(priv)) return PTR_ERR(priv); diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index efb2c1ceef98..957234272ef7 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -1205,10 +1205,13 @@ static int btt_rw_page(struct block_device *bdev, sector_t sector, struct page *page, int rw) { struct btt *btt = bdev->bd_disk->private_data; + int rc; - btt_do_bvec(btt, NULL, page, PAGE_CACHE_SIZE, 0, rw, sector); - page_endio(page, rw & WRITE, 0); - return 0; + rc = btt_do_bvec(btt, NULL, page, PAGE_CACHE_SIZE, 0, rw, sector); + if (rc == 0) + page_endio(page, rw & WRITE, 0); + + return rc; } diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index b7971d410b60..74e5360c53f0 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -88,7 +88,7 @@ static struct nvmem_config imx_ocotp_nvmem_config = { static const struct of_device_id imx_ocotp_dt_ids[] = { { .compatible = "fsl,imx6q-ocotp", (void *)128 }, - { .compatible = "fsl,imx6sl-ocotp", (void *)32 }, + { .compatible = "fsl,imx6sl-ocotp", (void *)64 }, { .compatible = "fsl,imx6sx-ocotp", (void *)128 }, { }, }; diff --git a/drivers/of/device.c b/drivers/of/device.c index e5f47cec75f3..97a280d50d6d 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -225,6 +225,7 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len) return tsize; } +EXPORT_SYMBOL_GPL(of_device_get_modalias); /** * of_device_uevent - Display OF related uevent information @@ -287,3 +288,4 @@ int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) return 0; } +EXPORT_SYMBOL_GPL(of_device_uevent_modalias); diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index 8e11fb2831cd..34f1d6b41fb9 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -741,6 +741,8 @@ ccio_map_single(struct device *dev, void *addr, size_t size, BUG_ON(!dev); ioc = GET_IOC(dev); + if (!ioc) + return DMA_ERROR_CODE; BUG_ON(size <= 0); @@ -805,6 +807,10 @@ ccio_unmap_single(struct device *dev, dma_addr_t iova, size_t size, BUG_ON(!dev); ioc = GET_IOC(dev); + if (!ioc) { + WARN_ON(!ioc); + return; + } DBG_RUN("%s() iovp 0x%lx/%x\n", __func__, (long)iova, size); @@ -908,6 +914,8 @@ ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents, BUG_ON(!dev); ioc = GET_IOC(dev); + if (!ioc) + return 0; DBG_RUN_SG("%s() START %d entries\n", __func__, nents); @@ -980,6 +988,10 @@ ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, BUG_ON(!dev); ioc = GET_IOC(dev); + if (!ioc) { + WARN_ON(!ioc); + return; + } DBG_RUN_SG("%s() START %d entries, %p,%x\n", __func__, nents, sg_virt(sglist), sglist->length); diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index a0580afe1713..7b0ca1551d7b 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -154,7 +154,10 @@ struct dino_device }; /* Looks nice and keeps the compiler happy */ -#define DINO_DEV(d) ((struct dino_device *) d) +#define DINO_DEV(d) ({ \ + void *__pdata = d; \ + BUG_ON(!__pdata); \ + (struct dino_device *)__pdata; }) /* diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index 42844c2bc065..d0c2759076a2 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -111,8 +111,10 @@ static u32 lba_t32; /* Looks nice and keeps the compiler happy */ -#define LBA_DEV(d) ((struct lba_device *) (d)) - +#define LBA_DEV(d) ({ \ + void *__pdata = d; \ + BUG_ON(!__pdata); \ + (struct lba_device *)__pdata; }) /* ** Only allow 8 subsidiary busses per LBA diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index 225049b492e5..d6326144ce01 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -691,6 +691,8 @@ static int sba_dma_supported( struct device *dev, u64 mask) return 0; ioc = GET_IOC(dev); + if (!ioc) + return 0; /* * check if mask is >= than the current max IO Virt Address @@ -722,6 +724,8 @@ sba_map_single(struct device *dev, void *addr, size_t size, int pide; ioc = GET_IOC(dev); + if (!ioc) + return DMA_ERROR_CODE; /* save offset bits */ offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK; @@ -803,6 +807,10 @@ sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size, DBG_RUN("%s() iovp 0x%lx/%x\n", __func__, (long) iova, size); ioc = GET_IOC(dev); + if (!ioc) { + WARN_ON(!ioc); + return; + } offset = iova & ~IOVP_MASK; iova ^= offset; /* clear offset bits */ size += offset; @@ -942,6 +950,8 @@ sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents, DBG_RUN_SG("%s() START %d entries\n", __func__, nents); ioc = GET_IOC(dev); + if (!ioc) + return 0; /* Fast path single entry scatterlists. */ if (nents == 1) { @@ -1027,6 +1037,10 @@ sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, __func__, nents, sg_virt(sglist), sglist->length); ioc = GET_IOC(dev); + if (!ioc) { + WARN_ON(!ioc); + return; + } #ifdef SBA_COLLECT_STATS ioc->usg_calls++; diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index 217c7ce3f57b..a741c9c7d115 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -278,6 +278,7 @@ #define PERST_PROPAGATION_DELAY_US_MIN 1000 #define PERST_PROPAGATION_DELAY_US_MAX 1005 +#define SWITCH_DELAY_MAX 20 #define REFCLK_STABILIZATION_DELAY_US_MIN 1000 #define REFCLK_STABILIZATION_DELAY_US_MAX 1005 #define LINK_UP_TIMEOUT_US_MIN 5000 @@ -595,7 +596,6 @@ struct msm_pcie_dev_t { bool cfg_access; spinlock_t cfg_lock; unsigned long irqsave_flags; - struct mutex enumerate_lock; struct mutex setup_lock; struct irq_domain *irq_domain; @@ -626,6 +626,7 @@ struct msm_pcie_dev_t { bool ext_ref_clk; bool common_phy; uint32_t ep_latency; + uint32_t switch_latency; uint32_t wr_halt_size; uint32_t cpl_timeout; uint32_t current_bdf; @@ -702,6 +703,9 @@ static u32 num_rc_on; /* global lock for PCIe common PHY */ static struct mutex com_phy_lock; +/* global lock for PCIe enumeration */ +static struct mutex enumerate_lock; + /* Table to track info of PCIe devices */ static struct msm_pcie_device_info msm_pcie_dev_tbl[MAX_RC_NUM * MAX_DEVICE_NUM]; @@ -1735,7 +1739,8 @@ static bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev) static int msm_pcie_restore_sec_config(struct msm_pcie_dev_t *dev) { - int ret, scm_ret; + int ret; + u64 scm_ret; if (!dev) { pr_err("PCIe: the input pcie dev is NULL.\n"); @@ -1745,7 +1750,7 @@ static int msm_pcie_restore_sec_config(struct msm_pcie_dev_t *dev) ret = scm_restore_sec_cfg(dev->scm_dev_id, 0, &scm_ret); if (ret || scm_ret) { PCIE_ERR(dev, - "PCIe: RC%d failed(%d) to restore sec config, scm_ret=%d\n", + "PCIe: RC%d failed(%d) to restore sec config, scm_ret=%llu\n", dev->rc_idx, ret, scm_ret); return ret ? ret : -EINVAL; } @@ -1984,6 +1989,8 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev) dev->common_phy); PCIE_DBG_FS(dev, "ep_latency: %dms\n", dev->ep_latency); + PCIE_DBG_FS(dev, "switch_latency: %dms\n", + dev->switch_latency); PCIE_DBG_FS(dev, "wr_halt_size: 0x%x\n", dev->wr_halt_size); PCIE_DBG_FS(dev, "cpl_timeout: 0x%x\n", @@ -4675,7 +4682,15 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options) goto link_fail; } - msleep(500); + if (dev->switch_latency) { + PCIE_DBG(dev, "switch_latency: %dms\n", + dev->switch_latency); + if (dev->switch_latency <= SWITCH_DELAY_MAX) + usleep_range(dev->switch_latency * 1000, + dev->switch_latency * 1000); + else + msleep(dev->switch_latency); + } msm_pcie_config_controller(dev); @@ -5037,7 +5052,7 @@ int msm_pcie_enumerate(u32 rc_idx) int ret = 0, bus_ret = 0, scan_ret = 0; struct msm_pcie_dev_t *dev = &msm_pcie_dev[rc_idx]; - mutex_lock(&dev->enumerate_lock); + mutex_lock(&enumerate_lock); PCIE_DBG(dev, "Enumerate RC%d\n", rc_idx); @@ -5156,7 +5171,7 @@ int msm_pcie_enumerate(u32 rc_idx) } out: - mutex_unlock(&dev->enumerate_lock); + mutex_unlock(&enumerate_lock); return ret; } @@ -6279,6 +6294,20 @@ static int msm_pcie_probe(struct platform_device *pdev) PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: ep-latency: 0x%x.\n", rc_idx, msm_pcie_dev[rc_idx].ep_latency); + msm_pcie_dev[rc_idx].switch_latency = 0; + ret = of_property_read_u32((&pdev->dev)->of_node, + "qcom,switch-latency", + &msm_pcie_dev[rc_idx].switch_latency); + + if (ret) + PCIE_DBG(&msm_pcie_dev[rc_idx], + "RC%d: switch-latency does not exist.\n", + rc_idx); + else + PCIE_DBG(&msm_pcie_dev[rc_idx], + "RC%d: switch-latency: 0x%x.\n", + rc_idx, msm_pcie_dev[rc_idx].switch_latency); + msm_pcie_dev[rc_idx].wr_halt_size = 0; ret = of_property_read_u32(pdev->dev.of_node, "qcom,wr-halt-size", @@ -6604,6 +6633,7 @@ int __init pcie_init(void) pcie_drv.rc_num = 0; mutex_init(&pcie_drv.drv_lock); mutex_init(&com_phy_lock); + mutex_init(&enumerate_lock); for (i = 0; i < MAX_RC_NUM; i++) { snprintf(rc_name, MAX_RC_NAME_LEN, "pcie%d-short", i); @@ -6638,7 +6668,6 @@ int __init pcie_init(void) rc_name, i); spin_lock_init(&msm_pcie_dev[i].cfg_lock); msm_pcie_dev[i].cfg_access = true; - mutex_init(&msm_pcie_dev[i].enumerate_lock); mutex_init(&msm_pcie_dev[i].setup_lock); mutex_init(&msm_pcie_dev[i].recovery_lock); spin_lock_init(&msm_pcie_dev[i].linkdown_lock); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index d7ffd66814bb..fca925543fae 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -945,6 +945,7 @@ static int pci_pm_thaw_noirq(struct device *dev) return pci_legacy_resume_early(dev); pci_update_current_state(pci_dev, PCI_D0); + pci_restore_state(pci_dev); if (drv && drv->pm && drv->pm->thaw_noirq) error = drv->pm->thaw_noirq(dev); diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 63ec68e6ac2a..39400dda27c2 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -552,6 +552,7 @@ static void armpmu_init(struct arm_pmu *armpmu) .stop = armpmu_stop, .read = armpmu_read, .filter_match = armpmu_filter_match, + .events_across_hotplug = 1, }; } diff --git a/drivers/pinctrl/freescale/pinctrl-mxs.c b/drivers/pinctrl/freescale/pinctrl-mxs.c index 6bbda6b4ab50..5da9c95dccb7 100644 --- a/drivers/pinctrl/freescale/pinctrl-mxs.c +++ b/drivers/pinctrl/freescale/pinctrl-mxs.c @@ -195,6 +195,16 @@ static int mxs_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, return 0; } +static void mxs_pinctrl_rmwl(u32 value, u32 mask, u8 shift, void __iomem *reg) +{ + u32 tmp; + + tmp = readl(reg); + tmp &= ~(mask << shift); + tmp |= value << shift; + writel(tmp, reg); +} + static int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector, unsigned group) { @@ -212,8 +222,7 @@ static int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector, reg += bank * 0x20 + pin / 16 * 0x10; shift = pin % 16 * 2; - writel(0x3 << shift, reg + CLR); - writel(g->muxsel[i] << shift, reg + SET); + mxs_pinctrl_rmwl(g->muxsel[i], 0x3, shift, reg); } return 0; @@ -280,8 +289,7 @@ static int mxs_pinconf_group_set(struct pinctrl_dev *pctldev, /* mA */ if (config & MA_PRESENT) { shift = pin % 8 * 4; - writel(0x3 << shift, reg + CLR); - writel(ma << shift, reg + SET); + mxs_pinctrl_rmwl(ma, 0x3, shift, reg); } /* vol */ diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c index 9677807db364..b505b87661f8 100644 --- a/drivers/pinctrl/meson/pinctrl-meson8b.c +++ b/drivers/pinctrl/meson/pinctrl-meson8b.c @@ -732,8 +732,8 @@ static const char * const sdxc_c_groups[] = { static const char * const nand_groups[] = { "nand_io", "nand_io_ce0", "nand_io_ce1", "nand_io_rb0", "nand_ale", "nand_cle", - "nand_wen_clk", "nand_ren_clk", "nand_dqs0", - "nand_dqs1" + "nand_wen_clk", "nand_ren_clk", "nand_dqs_0", + "nand_dqs_1" }; static const char * const nor_groups[] = { diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index 2b0d70217bbd..699efb1a8c45 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -543,6 +543,9 @@ static int sh_pfc_probe(struct platform_device *pdev) ret = info->ops->init(pfc); if (ret < 0) return ret; + + /* .init() may have overridden pfc->info */ + info = pfc->info; } /* Enable dummy states for those platforms without pinctrl support */ diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c index 87a4f44147c1..42ffa8708abc 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c @@ -1102,7 +1102,7 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_MSEL(IP6_5_3, FMIN_E, SEL_FM_4), PINMUX_IPSR_DATA(IP6_7_6, AUDIO_CLKOUT), PINMUX_IPSR_MSEL(IP6_7_6, MSIOF1_SS1_B, SEL_SOF1_1), - PINMUX_IPSR_MSEL(IP6_5_3, TX2, SEL_SCIF2_0), + PINMUX_IPSR_MSEL(IP6_7_6, TX2, SEL_SCIF2_0), PINMUX_IPSR_MSEL(IP6_7_6, SCIFA2_TXD, SEL_SCIFA2_0), PINMUX_IPSR_DATA(IP6_9_8, IRQ0), PINMUX_IPSR_MSEL(IP6_9_8, SCIFB1_RXD_D, SEL_SCIFB1_3), diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c index 90b973e15982..a7c81e988656 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c @@ -394,7 +394,7 @@ static const struct sunxi_desc_pin sun8i_a83t_pins[] = { SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 18), SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "owa")), /* DOUT */ + SUNXI_FUNCTION(0x3, "spdif")), /* DOUT */ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 19), SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out")), diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index b111a5904952..2ae2438032b7 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -1901,6 +1901,20 @@ int gsi_stop_channel(unsigned long chan_hdl) res = wait_for_completion_timeout(&ctx->compl, msecs_to_jiffies(GSI_STOP_CMD_TIMEOUT_MS)); if (res == 0) { + /* + * check channel state here in case the channel is stopped but + * the interrupt was not handled yet. + */ + val = gsi_readl(gsi_ctx->base + + GSI_EE_n_GSI_CH_k_CNTXT_0_OFFS(chan_hdl, + gsi_ctx->per.ee)); + ctx->state = (val & + GSI_EE_n_GSI_CH_k_CNTXT_0_CHSTATE_BMSK) >> + GSI_EE_n_GSI_CH_k_CNTXT_0_CHSTATE_SHFT; + if (ctx->state == GSI_CHAN_STATE_STOPPED) { + res = GSI_STATUS_SUCCESS; + goto free_lock; + } GSIDBG("chan_hdl=%lu timed out\n", chan_hdl); res = -GSI_STATUS_TIMED_OUT; goto free_lock; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c index 51806cec1e4d..49aa7f25347d 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c @@ -649,8 +649,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, return 0; ipa_insert_failed: - if (offset) - list_move(&offset->link, + list_move(&offset->link, &htbl->head_free_offset_list[offset->bin]); entry->offset_entry = NULL; list_del(&entry->link); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c index dd591407d10f..5228b2db1410 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1436,6 +1436,66 @@ struct elem_info ipa_fltr_installed_notif_req_msg_data_v01_ei[] = { start_ipv6_filter_idx), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof( + struct ipa_fltr_installed_notif_req_msg_v01, + rule_id_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof( + struct ipa_fltr_installed_notif_req_msg_v01, + rule_id_len), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = QMI_IPA_MAX_FILTERS_V01, + .elem_size = sizeof(uint32_t), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x17, + .offset = offsetof( + struct ipa_fltr_installed_notif_req_msg_v01, + rule_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof( + struct ipa_fltr_installed_notif_req_msg_v01, + dst_pipe_id_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof( + struct ipa_fltr_installed_notif_req_msg_v01, + dst_pipe_id_len), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = QMI_IPA_MAX_CLIENT_DST_PIPES_V01, + .elem_size = sizeof(uint32_t), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x18, + .offset = offsetof( + struct ipa_fltr_installed_notif_req_msg_v01, + dst_pipe_id), + }, + { .data_type = QMI_EOTI, .is_array = NO_ARRAY, .tlv_type = QMI_COMMON_TLV_TYPE, diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 0a6f79356a77..293a60a60881 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -1406,7 +1406,7 @@ int ipa2_put_rt_tbl(u32 rt_tbl_hdl) { struct ipa_rt_tbl *entry; enum ipa_ip_type ip = IPA_IP_MAX; - int result; + int result = 0; mutex_lock(&ipa_ctx->lock); entry = ipa_id_find(rt_tbl_hdl); diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 834712a71ac6..5dbd43b44540 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1878,7 +1878,9 @@ void q6_deinitialize_rm(void) if (ret < 0) IPAWANERR("Error deleting resource %d, ret=%d\n", IPA_RM_RESOURCE_Q6_PROD, ret); - destroy_workqueue(ipa_rm_q6_workqueue); + + if (ipa_rm_q6_workqueue) + destroy_workqueue(ipa_rm_q6_workqueue); } static void wake_tx_queue(struct work_struct *work) @@ -2187,7 +2189,10 @@ timer_init_err: IPAWANERR("Error deleting resource %d, ret=%d\n", IPA_RM_RESOURCE_WWAN_0_PROD, ret); create_rsrc_err: - q6_deinitialize_rm(); + + if (!atomic_read(&is_ssr)) + q6_deinitialize_rm(); + q6_init_err: free_netdev(ipa_netdevs[0]); ipa_netdevs[0] = NULL; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 7ed83fb74fcc..2615db4e9755 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -1654,6 +1654,21 @@ int ipa3_setup_dflt_rt_tables(void) return 0; } +static int ipa3_clkon_cfg_wa(void) +{ + struct ipahal_reg_clkon_cfg clkon_cfg = { 0 }; + int ret = 0; + + clkon_cfg.cgc_open_misc = 1; + + if (ipa3_cfg_clkon_cfg(&clkon_cfg)) { + IPAERR("fail to set cgc_open_misc = 1\n"); + ret = -EPERM; + } + + return ret; +} + static int ipa3_setup_exception_path(void) { struct ipa_ioc_add_hdr *hdr; @@ -2233,7 +2248,6 @@ static int ipa3_q6_set_ex_path_to_apps(void) struct ipahal_imm_cmd_register_write reg_write; struct ipahal_imm_cmd_pyld *cmd_pyld; int retval; - struct ipahal_reg_valmask valmask; desc = kcalloc(ipa3_ctx->ipa_num_pipes, sizeof(struct ipa3_desc), GFP_KERNEL); @@ -2248,40 +2262,10 @@ static int ipa3_q6_set_ex_path_to_apps(void) if (ep_idx == -1) continue; - if (ipa3_ctx->ep[ep_idx].valid && - ipa3_ctx->ep[ep_idx].skip_ep_cfg) { - BUG_ON(num_descs >= ipa3_ctx->ipa_num_pipes); - - reg_write.skip_pipeline_clear = false; - reg_write.pipeline_clear_options = - IPAHAL_HPS_CLEAR; - reg_write.offset = - ipahal_get_reg_n_ofst(IPA_ENDP_STATUS_n, - ep_idx); - ipahal_get_status_ep_valmask( - ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS), - &valmask); - reg_write.value = valmask.val; - reg_write.value_mask = valmask.mask; - cmd_pyld = ipahal_construct_imm_cmd( - IPA_IMM_CMD_REGISTER_WRITE, ®_write, false); - if (!cmd_pyld) { - IPAERR("fail construct register_write cmd\n"); - BUG(); - } - - desc[num_descs].opcode = ipahal_imm_cmd_get_opcode( - IPA_IMM_CMD_REGISTER_WRITE); - desc[num_descs].type = IPA_IMM_CMD_DESC; - desc[num_descs].callback = ipa3_destroy_imm; - desc[num_descs].user1 = cmd_pyld; - desc[num_descs].pyld = cmd_pyld->data; - desc[num_descs].len = cmd_pyld->len; - num_descs++; - } - - /* disable statuses for modem producers */ - if (IPA_CLIENT_IS_Q6_PROD(client_idx)) { + /* disable statuses for all modem controlled prod pipes */ + if (IPA_CLIENT_IS_Q6_PROD(client_idx) || + (ipa3_ctx->ep[ep_idx].valid && + ipa3_ctx->ep[ep_idx].skip_ep_cfg)) { ipa_assert_on(num_descs >= ipa3_ctx->ipa_num_pipes); reg_write.skip_pipeline_clear = false; @@ -3962,6 +3946,8 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p, /* Prevent consequent calls from trying to load the FW again. */ if (ipa3_ctx->ipa_initialization_complete) return 0; + /* move proxy vote for modem on ipa3_post_init */ + IPA_ACTIVE_CLIENTS_INC_SPECIAL("PROXY_CLK_VOTE"); if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) { memset(&gsi_props, 0, sizeof(gsi_props)); @@ -4063,8 +4049,14 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p, ipa3_trigger_ipa_ready_cbs(); complete_all(&ipa3_ctx->init_completion_obj); - pr_info("IPA driver initialization was successful.\n"); + /* WA to disable MISC clock gating for IPA_HW_v3_1 */ + if (ipa3_ctx->ipa_hw_type == IPA_HW_v3_1) { + pr_info(" WA to set cgc_open_misc = 1\n"); + ipa3_clkon_cfg_wa(); + } + + pr_info("IPA driver initialization was successful\n"); return 0; fail_teth_bridge_driver_init: @@ -4083,12 +4075,12 @@ static void ipa3_post_init_wq(struct work_struct *work) ipa3_post_init(&ipa3_res, ipa3_ctx->dev); } -static int ipa3_trigger_fw_loading_mdms(void) +static int ipa3_manual_load_ipa_fws(void) { int result; const struct firmware *fw; - IPADBG("FW loading process initiated\n"); + IPADBG("Manual FW loading process initiated\n"); result = request_firmware(&fw, IPA_FWS_PATH, ipa3_ctx->dev); if (result < 0) { @@ -4104,7 +4096,7 @@ static int ipa3_trigger_fw_loading_mdms(void) result = ipa3_load_fws(fw, ipa3_res.transport_mem_base); if (result) { - IPAERR("IPA FWs loading has failed\n"); + IPAERR("Manual IPA FWs loading has failed\n"); release_firmware(fw); return result; } @@ -4119,15 +4111,15 @@ static int ipa3_trigger_fw_loading_mdms(void) release_firmware(fw); - IPADBG("FW loading process is complete\n"); + IPADBG("Manual FW loading process is complete\n"); return 0; } -static int ipa3_trigger_fw_loading_msms(void) +static int ipa3_pil_load_ipa_fws(void) { void *subsystem_get_retval = NULL; - IPADBG("FW loading process initiated\n"); + IPADBG("PIL FW loading process initiated\n"); subsystem_get_retval = subsystem_get(IPA_SUBSYSTEM_NAME); if (IS_ERR_OR_NULL(subsystem_get_retval)) { @@ -4135,7 +4127,7 @@ static int ipa3_trigger_fw_loading_msms(void) return -EINVAL; } - IPADBG("FW loading process is complete\n"); + IPADBG("PIL FW loading process is complete\n"); return 0; } @@ -4165,25 +4157,27 @@ static ssize_t ipa3_write(struct file *file, const char __user *buf, * We will trigger the process only if we're in GSI mode, otherwise, * we just ignore the write. */ - if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) { - IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + if (ipa3_ctx->transport_prototype != IPA_TRANSPORT_TYPE_GSI) + return count; - if (ipa3_is_msm_device()) - result = ipa3_trigger_fw_loading_msms(); - else - result = ipa3_trigger_fw_loading_mdms(); - /* No IPAv3.x chipsets that don't support FW loading */ + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); - IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + if (ipa3_is_msm_device() || (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5)) + result = ipa3_pil_load_ipa_fws(); + else + result = ipa3_manual_load_ipa_fws(); - if (result) { - IPAERR("FW loading process has failed\n"); - return result; - } else { - queue_work(ipa3_ctx->transport_power_mgmt_wq, - &ipa3_post_init_work); - } + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + + if (result) { + IPAERR("IPA FW loading process has failed\n"); + return result; } + + queue_work(ipa3_ctx->transport_power_mgmt_wq, + &ipa3_post_init_work); + IPADBG("IPA FW loaded successfully\n"); + return count; } @@ -4275,7 +4269,6 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, int i; struct ipa3_flt_tbl *flt_tbl; struct ipa3_rt_tbl_set *rset; - struct ipa_active_client_logging_info log_info; IPADBG("IPA Driver initialization started\n"); @@ -4465,8 +4458,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, mutex_init(&ipa3_ctx->ipa3_active_clients.mutex); spin_lock_init(&ipa3_ctx->ipa3_active_clients.spinlock); - IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, "PROXY_CLK_VOTE"); - ipa3_active_clients_log_inc(&log_info, false); + /* move proxy vote for modem to ipa3_post_init() */ ipa3_ctx->ipa3_active_clients.cnt = 1; /* Assign resource limitation to each group */ @@ -4629,6 +4621,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, mutex_init(&ipa3_ctx->lock); mutex_init(&ipa3_ctx->nat_mem.lock); + mutex_init(&ipa3_ctx->q6_proxy_clk_vote_mutex); idr_init(&ipa3_ctx->ipa_idr); spin_lock_init(&ipa3_ctx->idr_lock); @@ -4702,7 +4695,6 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, init_completion(&ipa3_ctx->init_completion_obj); init_completion(&ipa3_ctx->uc_loaded_completion_obj); - /* * For GSI, we can't register the GSI driver yet, as it expects * the GSI FW to be up and running before the registration. @@ -4745,6 +4737,8 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, IPADBG("ipa cdev added successful. major:%d minor:%d\n", MAJOR(ipa3_ctx->dev_num), MINOR(ipa3_ctx->dev_num)); + /* proxy vote for motem is added in ipa3_post_init() phase */ + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return 0; fail_cdev_add: @@ -5451,6 +5445,10 @@ static int ipa3_smp2p_probe(struct device *dev) struct device_node *node = dev->of_node; int res; + if (ipa3_ctx == NULL) { + IPAERR("ipa3_ctx was not initialized\n"); + return -ENXIO; + } IPADBG("node->name=%s\n", node->name); if (strcmp("qcom,smp2pgpio_map_ipa_1_out", node->name) == 0) { res = of_get_gpio(node, 0); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 5fe425ea7655..034b7fc45d8d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -2093,6 +2093,8 @@ static void ipa3_cleanup_wlan_rx_common_cache(void) struct ipa3_rx_pkt_wrapper *rx_pkt; struct ipa3_rx_pkt_wrapper *tmp; + spin_lock_bh(&ipa3_ctx->wc_memb.wlan_spinlock); + list_for_each_entry_safe(rx_pkt, tmp, &ipa3_ctx->wc_memb.wlan_comm_desc_list, link) { list_del(&rx_pkt->link); @@ -2113,6 +2115,8 @@ static void ipa3_cleanup_wlan_rx_common_cache(void) IPAERR("wlan comm buff total cnt: %d\n", ipa3_ctx->wc_memb.wlan_comm_total_cnt); + spin_unlock_bh(&ipa3_ctx->wc_memb.wlan_spinlock); + } static void ipa3_alloc_wlan_rx_common_cache(u32 size) @@ -2150,11 +2154,13 @@ static void ipa3_alloc_wlan_rx_common_cache(u32 size) goto fail_dma_mapping; } + spin_lock_bh(&ipa3_ctx->wc_memb.wlan_spinlock); list_add_tail(&rx_pkt->link, &ipa3_ctx->wc_memb.wlan_comm_desc_list); rx_len_cached = ++ipa3_ctx->wc_memb.wlan_comm_total_cnt; ipa3_ctx->wc_memb.wlan_comm_free_cnt++; + spin_unlock_bh(&ipa3_ctx->wc_memb.wlan_spinlock); } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c index f8529faf159d..ce35ba02154d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c @@ -359,7 +359,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, entry->hdr = hdr_entry; if (add_ref_hdr) hdr_entry->ref_cnt++; - entry->cookie = IPA_HDR_COOKIE; + entry->cookie = IPA_PROC_HDR_COOKIE; needed_len = ipahal_get_proc_ctx_needed_len(proc_ctx->type); @@ -426,8 +426,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, return 0; ipa_insert_failed: - if (offset) - list_move(&offset->link, + list_move(&offset->link, &htbl->head_free_offset_list[offset->bin]); entry->offset_entry = NULL; list_del(&entry->link); @@ -610,7 +609,7 @@ static int __ipa3_del_hdr_proc_ctx(u32 proc_ctx_hdl, struct ipa3_hdr_proc_ctx_tbl *htbl = &ipa3_ctx->hdr_proc_ctx_tbl; entry = ipa3_id_find(proc_ctx_hdl); - if (!entry || (entry->cookie != IPA_HDR_COOKIE)) { + if (!entry || (entry->cookie != IPA_PROC_HDR_COOKIE)) { IPAERR_RL("bad parm\n"); return -EINVAL; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index a890e88a8f61..f12eb6ded556 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1231,6 +1231,7 @@ struct ipa3_context { u32 enable_clock_scaling; u32 curr_ipa_clk_rate; bool q6_proxy_clk_vote_valid; + struct mutex q6_proxy_clk_vote_mutex; u32 ipa_num_pipes; struct ipa3_wlan_comm_memb wc_memb; @@ -1877,6 +1878,7 @@ void ipa3_dump_buff_internal(void *base, dma_addr_t phy_base, u32 size); #else #define IPA_DUMP_BUFF(base, phy_base, size) #endif +int ipa3_cfg_clkon_cfg(struct ipahal_reg_clkon_cfg *clkon_cfg); int ipa3_init_mem_partition(struct device_node *dev_node); int ipa3_controller_static_bind(struct ipa3_controller *controller, enum ipa_hw_type ipa_hw_type); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c index 3267e0e83a82..0bf2be7f8463 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c @@ -630,6 +630,8 @@ int ipa3_mhi_destroy_channel(enum ipa_client_type client) } ep = &ipa3_ctx->ep[ipa_ep_idx]; + IPA_ACTIVE_CLIENTS_INC_EP(client); + IPA_MHI_DBG("reset event ring (hdl: %lu, ep: %d)\n", ep->gsi_evt_ring_hdl, ipa_ep_idx); @@ -651,8 +653,10 @@ int ipa3_mhi_destroy_channel(enum ipa_client_type client) goto fail; } + IPA_ACTIVE_CLIENTS_DEC_EP(client); return 0; fail: + IPA_ACTIVE_CLIENTS_DEC_EP(client); return res; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c index 690a9db67ff0..571852c076ea 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c @@ -807,6 +807,11 @@ int ipa3_qmi_filter_notify_send( return -EINVAL; } + if (req->source_pipe_index == -1) { + IPAWANERR("Source pipe index invalid\n"); + return -EINVAL; + } + mutex_lock(&ipa3_qmi_lock); if (ipa3_qmi_ctx != NULL) { /* cache the qmi_filter_request */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h index d5d850309696..e6f1e2ce0b75 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h @@ -121,6 +121,31 @@ extern struct elem_info ipa3_init_modem_driver_cmplt_resp_msg_data_v01_ei[]; extern struct elem_info ipa3_install_fltr_rule_req_ex_msg_data_v01_ei[]; extern struct elem_info ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[]; + extern struct elem_info + ipa3_install_fltr_rule_req_ex_msg_data_v01_ei[]; + extern struct elem_info + ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[]; + extern struct elem_info + ipa3_ul_firewall_rule_type_data_v01_ei[]; + extern struct elem_info + ipa3_ul_firewall_config_result_type_data_v01_ei[]; + extern struct elem_info + ipa3_per_client_stats_info_type_data_v01_ei[]; + extern struct elem_info + ipa3_enable_per_client_stats_req_msg_data_v01_ei[]; + extern struct elem_info + ipa3_enable_per_client_stats_resp_msg_data_v01_ei[]; + extern struct elem_info + ipa3_get_stats_per_client_req_msg_data_v01_ei[]; + extern struct elem_info + ipa3_get_stats_per_client_resp_msg_data_v01_ei[]; + extern struct elem_info + ipa3_configure_ul_firewall_rules_req_msg_data_v01_ei[]; + extern struct elem_info + ipa3_configure_ul_firewall_rules_resp_msg_data_v01_ei[]; + extern struct elem_info + ipa3_configure_ul_firewall_rules_ind_msg_data_v01_ei[]; + /** * struct ipa3_rmnet_context - IPA rmnet context * @ipa_rmnet_ssr: support modem SSR diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c index 6a5cb4891c02..746863732dc5 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -16,6 +16,8 @@ #include <soc/qcom/msm_qmi_interface.h> +#include "ipa_qmi_service.h" + /* Type Definitions */ static struct elem_info ipa3_hdr_tbl_info_type_data_v01_ei[] = { { @@ -1756,6 +1758,36 @@ struct elem_info ipa3_fltr_installed_notif_req_msg_data_v01_ei[] = { rule_id), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof( + struct ipa_fltr_installed_notif_req_msg_v01, + dst_pipe_id_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof( + struct ipa_fltr_installed_notif_req_msg_v01, + dst_pipe_id_len), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = QMI_IPA_MAX_CLIENT_DST_PIPES_V01, + .elem_size = sizeof(uint32_t), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x18, + .offset = offsetof( + struct ipa_fltr_installed_notif_req_msg_v01, + dst_pipe_id), + }, + { .data_type = QMI_EOTI, .is_array = NO_ARRAY, .tlv_type = QMI_COMMON_TLV_TYPE, @@ -2923,3 +2955,435 @@ struct elem_info ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[] = { .tlv_type = QMI_COMMON_TLV_TYPE, }, }; + +struct elem_info ipa3_per_client_stats_info_type_data_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + client_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + src_pipe_id), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + num_ul_ipv4_bytes), + + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + num_ul_ipv6_bytes), + + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + num_dl_ipv4_bytes), + + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + num_dl_ipv6_bytes), + + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + num_ul_ipv4_pkts), + + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + num_ul_ipv6_pkts), + + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + num_dl_ipv4_pkts), + + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_per_client_stats_info_type_v01, + num_dl_ipv6_pkts), + + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info ipa3_ul_firewall_rule_type_data_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_ul_firewall_rule_type_v01, + ip_type), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct ipa_filter_rule_type_v01), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof(struct ipa_ul_firewall_rule_type_v01, + filter_rule), + .ei_array = ipa3_filter_rule_type_data_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info ipa3_ul_firewall_config_result_type_data_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_ul_firewall_config_result_type_v01, + is_success), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof( + struct ipa_ul_firewall_config_result_type_v01, + mux_id), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info ipa3_enable_per_client_stats_req_msg_data_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + ipa_enable_per_client_stats_req_msg_v01, + enable_per_client_stats), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info ipa3_enable_per_client_stats_resp_msg_data_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct ipa_enable_per_client_stats_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info ipa3_get_stats_per_client_req_msg_data_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof( + struct ipa_get_stats_per_client_req_msg_v01, + client_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct ipa_get_stats_per_client_req_msg_v01, + src_pipe_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct ipa_get_stats_per_client_req_msg_v01, + reset_stats_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct ipa_get_stats_per_client_req_msg_v01, + reset_stats), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info ipa3_get_stats_per_client_resp_msg_data_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct ipa_get_stats_per_client_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct ipa_get_stats_per_client_resp_msg_v01, + per_client_stats_list_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct ipa_get_stats_per_client_resp_msg_v01, + per_client_stats_list_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_IPA_MAX_PER_CLIENTS_V01, + .elem_size = + sizeof(struct ipa_per_client_stats_info_type_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct ipa_get_stats_per_client_resp_msg_v01, + per_client_stats_list), + .ei_array = + ipa3_per_client_stats_info_type_data_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info + ipa3_configure_ul_firewall_rules_req_msg_data_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x1, + .offset = offsetof( + struct ipa_configure_ul_firewall_rules_req_msg_v01, + firewall_rules_list_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_IPA_MAX_UL_FIREWALL_RULES_V01, + .elem_size = sizeof(struct ipa_ul_firewall_rule_type_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x1, + .offset = offsetof( + struct ipa_configure_ul_firewall_rules_req_msg_v01, + firewall_rules_list), + .ei_array = + ipa3_ul_firewall_rule_type_data_v01_ei, + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x2, + .offset = offsetof( + struct ipa_configure_ul_firewall_rules_req_msg_v01, + mux_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct ipa_configure_ul_firewall_rules_req_msg_v01, + disable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct ipa_configure_ul_firewall_rules_req_msg_v01, + disable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof( + struct ipa_configure_ul_firewall_rules_req_msg_v01, + are_blacklist_filters_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof( + struct ipa_configure_ul_firewall_rules_req_msg_v01, + are_blacklist_filters), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info + ipa3_configure_ul_firewall_rules_resp_msg_data_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct ipa_configure_ul_firewall_rules_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info + ipa3_configure_ul_firewall_rules_ind_msg_data_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof( + struct ipa_ul_firewall_config_result_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof( + struct ipa_configure_ul_firewall_rules_ind_msg_v01, + result), + .ei_array = + ipa3_ul_firewall_config_result_type_data_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index d6eeb3bc777e..ff57e3bd48f0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -1486,7 +1486,7 @@ int ipa3_put_rt_tbl(u32 rt_tbl_hdl) { struct ipa3_rt_tbl *entry; enum ipa_ip_type ip = IPA_IP_MAX; - int result; + int result = 0; mutex_lock(&ipa3_ctx->lock); entry = ipa3_id_find(rt_tbl_hdl); @@ -1508,6 +1508,7 @@ int ipa3_put_rt_tbl(u32 rt_tbl_hdl) ip = IPA_IP_v6; else { WARN_ON(1); + result = -EINVAL; goto ret; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 1fa1196dda63..4979f62b928f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -795,6 +795,28 @@ void _ipa_sram_settings_read_v3_0(void) } /** + * ipa3_cfg_clkon_cfg() - configure IPA clkon_cfg + * @clkon_cfg: IPA clkon_cfg + * + * Return codes: + * 0: success + */ +int ipa3_cfg_clkon_cfg(struct ipahal_reg_clkon_cfg *clkon_cfg) +{ + + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + + IPADBG("cgc_open_misc = %d\n", + clkon_cfg->cgc_open_misc); + + ipahal_write_reg_fields(IPA_CLKON_CFG, clkon_cfg); + + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + + return 0; +} + +/** * ipa3_cfg_route() - configure IPA route * @route: IPA route * @@ -2839,7 +2861,7 @@ static int ipa3_tag_generate_force_close_desc(struct ipa3_desc desc[], IPAHAL_FULL_PIPELINE_CLEAR; reg_write_agg_close.offset = ipahal_get_reg_ofst(IPA_AGGR_FORCE_CLOSE); - ipahal_get_aggr_force_close_valmask(1<<i, &valmask); + ipahal_get_aggr_force_close_valmask(i, &valmask); reg_write_agg_close.value = valmask.val; reg_write_agg_close.value_mask = valmask.mask; cmd_pyld = ipahal_construct_imm_cmd(IPA_IMM_CMD_REGISTER_WRITE, @@ -2961,10 +2983,15 @@ bool ipa3_is_client_handle_valid(u32 clnt_hdl) */ void ipa3_proxy_clk_unvote(void) { - if (ipa3_is_ready() && ipa3_ctx->q6_proxy_clk_vote_valid) { + if (!ipa3_is_ready()) + return; + + mutex_lock(&ipa3_ctx->q6_proxy_clk_vote_mutex); + if (ipa3_ctx->q6_proxy_clk_vote_valid) { IPA_ACTIVE_CLIENTS_DEC_SPECIAL("PROXY_CLK_VOTE"); ipa3_ctx->q6_proxy_clk_vote_valid = false; } + mutex_unlock(&ipa3_ctx->q6_proxy_clk_vote_mutex); } /** @@ -2974,10 +3001,15 @@ void ipa3_proxy_clk_unvote(void) */ void ipa3_proxy_clk_vote(void) { - if (ipa3_is_ready() && !ipa3_ctx->q6_proxy_clk_vote_valid) { + if (!ipa3_is_ready()) + return; + + mutex_lock(&ipa3_ctx->q6_proxy_clk_vote_mutex); + if (!ipa3_ctx->q6_proxy_clk_vote_valid) { IPA_ACTIVE_CLIENTS_INC_SPECIAL("PROXY_CLK_VOTE"); ipa3_ctx->q6_proxy_clk_vote_valid = true; } + mutex_unlock(&ipa3_ctx->q6_proxy_clk_vote_mutex); } /** diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c index 585f9e6bd492..d0aa42c81750 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c @@ -19,6 +19,7 @@ #include "ipahal_reg_i.h" static const char *ipareg_name_to_str[IPA_REG_MAX] = { + __stringify(IPA_CLKON_CFG), __stringify(IPA_ROUTE), __stringify(IPA_IRQ_STTS_EE_n), __stringify(IPA_IRQ_EN_EE_n), @@ -858,6 +859,18 @@ static void ipareg_construct_endp_init_hdr_n(enum ipahal_reg_name reg, IPA_ENDP_INIT_HDR_n_HDR_LEN_BMSK); } +static void ipareg_construct_clkon_cfg(enum ipahal_reg_name reg, + const void *fields, u32 *val) +{ + struct ipahal_reg_clkon_cfg *clkon_cfg; + + clkon_cfg = (struct ipahal_reg_clkon_cfg *)fields; + + IPA_SETFIELD_IN_REG(*val, clkon_cfg->cgc_open_misc, + IPA_CLKON_CFG_CGC_OPEN_MISC_SHFT, + IPA_CLKON_CFG_CGC_OPEN_MISC_BMSK); +} + static void ipareg_construct_route(enum ipahal_reg_name reg, const void *fields, u32 *val) { @@ -1159,6 +1172,9 @@ static struct ipahal_reg_obj ipahal_reg_objs[IPA_HW_MAX][IPA_REG_MAX] = { /* IPAv3.1 */ + [IPA_HW_v3_1][IPA_CLKON_CFG] = { + ipareg_construct_clkon_cfg, ipareg_parse_dummy, + 0x00000044, 0}, [IPA_HW_v3_1][IPA_IRQ_SUSPEND_INFO_EE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, 0x00003030, 0x1000}, @@ -1560,6 +1576,11 @@ void ipahal_get_aggr_force_close_valmask(int ep_idx, IPA_AGGR_FORCE_CLOSE_AGGR_FORCE_CLOSE_PIPE_BITMAP_BMSK_V3_5; } + if (ep_idx > (sizeof(valmask->val) * 8 - 1)) { + IPAHAL_ERR("too big ep_idx %d\n", ep_idx); + ipa_assert(); + return; + } IPA_SETFIELD_IN_REG(valmask->val, 1 << ep_idx, shft, bmsk); valmask->mask = bmsk << shft; } @@ -1591,20 +1612,3 @@ void ipahal_get_fltrt_hash_flush_valmask( valmask->mask = valmask->val; } - -void ipahal_get_status_ep_valmask(int pipe_num, - struct ipahal_reg_valmask *valmask) -{ - if (!valmask) { - IPAHAL_ERR("Input error\n"); - return; - } - - valmask->val = - (pipe_num & IPA_ENDP_STATUS_n_STATUS_ENDP_BMSK) << - IPA_ENDP_STATUS_n_STATUS_ENDP_SHFT; - - valmask->mask = - IPA_ENDP_STATUS_n_STATUS_ENDP_BMSK << - IPA_ENDP_STATUS_n_STATUS_ENDP_SHFT; -} diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h index c37415f13380..9f32c071cb68 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h @@ -22,6 +22,7 @@ * array as well. */ enum ipahal_reg_name { + IPA_CLKON_CFG, IPA_ROUTE, IPA_IRQ_STTS_EE_n, IPA_IRQ_EN_EE_n, @@ -90,6 +91,14 @@ enum ipahal_reg_name { }; /* + * struct ipahal_reg_clkon_cfg - IPA clock on configuration register + * @cgc_open_misc: clock gating needs for MISC + */ +struct ipahal_reg_clkon_cfg { + u32 cgc_open_misc; +}; + +/* * struct ipahal_reg_route - IPA route register * @route_dis: route disable * @route_def_pipe: route default pipe @@ -458,8 +467,6 @@ void ipahal_get_aggr_force_close_valmask(int ep_idx, void ipahal_get_fltrt_hash_flush_valmask( struct ipahal_reg_fltrt_hash_flush *flush, struct ipahal_reg_valmask *valmask); -void ipahal_get_status_ep_valmask(int pipe_num, - struct ipahal_reg_valmask *valmask); #endif /* _IPAHAL_REG_H_ */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg_i.h index ac97e5ac0494..37458c4d0697 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg_i.h @@ -21,6 +21,9 @@ int ipahal_reg_init(enum ipa_hw_type ipa_hw_type); #define IPA_GETFIELD_FROM_REG(reg, shift, mask) \ (((reg) & (mask)) >> (shift)) +/* IPA_CLKON_CFG register */ +#define IPA_CLKON_CFG_CGC_OPEN_MISC_SHFT 0x3 +#define IPA_CLKON_CFG_CGC_OPEN_MISC_BMSK 0x8 /* IPA_ROUTE register */ #define IPA_ROUTE_ROUTE_DIS_SHFT 0x0 diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 039bc7da5153..8fbde6675070 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -702,6 +702,11 @@ static int ipa3_wwan_add_ul_flt_rule_to_ipa(void) /* send ipa_fltr_installed_notif_req_msg_v01 to Q6*/ req->source_pipe_index = ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_PROD); + if (req->source_pipe_index == IPA_EP_NOT_ALLOCATED) { + IPAWANERR("ep mapping failed\n"); + retval = -EFAULT; + } + req->install_status = QMI_RESULT_SUCCESS_V01; req->rule_id_valid = 1; req->rule_id_len = rmnet_ipa3_ctx->num_q6_rules; @@ -1947,7 +1952,9 @@ void ipa3_q6_deinitialize_rm(void) if (ret < 0) IPAWANERR("Error deleting resource %d, ret=%d\n", IPA_RM_RESOURCE_Q6_PROD, ret); - destroy_workqueue(rmnet_ipa3_ctx->rm_q6_wq); + + if (rmnet_ipa3_ctx->rm_q6_wq) + destroy_workqueue(rmnet_ipa3_ctx->rm_q6_wq); } static void ipa3_wake_tx_queue(struct work_struct *work) @@ -2287,7 +2294,10 @@ timer_init_err: IPAWANERR("Error deleting resource %d, ret=%d\n", IPA_RM_RESOURCE_WWAN_0_PROD, ret); create_rsrc_err: - ipa3_q6_deinitialize_rm(); + + if (!atomic_read(&rmnet_ipa3_ctx->is_ssr)) + ipa3_q6_deinitialize_rm(); + q6_init_err: free_netdev(dev); rmnet_ipa3_ctx->wwan_priv = NULL; diff --git a/drivers/platform/msm/mhi/mhi_iface.c b/drivers/platform/msm/mhi/mhi_iface.c index a5936ea5a6aa..ce6d1257cfbb 100644 --- a/drivers/platform/msm/mhi/mhi_iface.c +++ b/drivers/platform/msm/mhi/mhi_iface.c @@ -509,7 +509,7 @@ static int __exit mhi_plat_remove(struct platform_device *pdev) static int __init mhi_init(void) { - int r; + int r = -EAGAIN; struct mhi_device_driver *mhi_dev_drv; mhi_dev_drv = kmalloc(sizeof(*mhi_dev_drv), GFP_KERNEL); diff --git a/drivers/platform/msm/mhi/mhi_init.c b/drivers/platform/msm/mhi/mhi_init.c index b6edf707798b..a95579241524 100644 --- a/drivers/platform/msm/mhi/mhi_init.c +++ b/drivers/platform/msm/mhi/mhi_init.c @@ -141,7 +141,7 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt) size_t mhi_mem_index = 0, ring_len; void *dev_mem_start; dma_addr_t dma_dev_mem_start; - int i, r; + int i; mhi_dev_ctxt->dev_space.dev_mem_len = calculate_mhi_space(mhi_dev_ctxt); @@ -244,7 +244,7 @@ err_ev_alloc: mhi_dev_ctxt->dev_space.dev_mem_len, mhi_dev_ctxt->dev_space.dev_mem_start, mhi_dev_ctxt->dev_space.dma_dev_mem_start); - return r; + return -EFAULT; } static int mhi_init_events(struct mhi_device_ctxt *mhi_dev_ctxt) diff --git a/drivers/platform/msm/mhi/mhi_mmio_ops.c b/drivers/platform/msm/mhi/mhi_mmio_ops.c index a991a2e68b34..18d0334ce1ec 100644 --- a/drivers/platform/msm/mhi/mhi_mmio_ops.c +++ b/drivers/platform/msm/mhi/mhi_mmio_ops.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -109,7 +109,6 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt) u64 pcie_dword_val = 0; u32 pcie_word_val = 0; u32 i = 0; - int ret_val; mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "~~~ Initializing MMIO ~~~\n"); @@ -131,7 +130,7 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt) if (mhi_dev_ctxt->core.mhi_ver != MHI_VERSION) { mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Bad MMIO version, 0x%x\n", mhi_dev_ctxt->core.mhi_ver); - return ret_val; + return -ENXIO; } /* Enable the channels */ diff --git a/drivers/platform/msm/mhi/mhi_sys.c b/drivers/platform/msm/mhi/mhi_sys.c index 1d9282627d4e..b1434daf1f60 100644 --- a/drivers/platform/msm/mhi/mhi_sys.c +++ b/drivers/platform/msm/mhi/mhi_sys.c @@ -329,7 +329,7 @@ uintptr_t mhi_p2v_addr(struct mhi_device_ctxt *mhi_dev_ctxt, enum MHI_RING_TYPE type, u32 chan, uintptr_t phy_ptr) { - uintptr_t virtual_ptr; + uintptr_t virtual_ptr = 0; struct mhi_ring_ctxt *cs = &mhi_dev_ctxt->dev_space.ring_ctxt; switch (type) { @@ -358,7 +358,7 @@ dma_addr_t mhi_v2p_addr(struct mhi_device_ctxt *mhi_dev_ctxt, enum MHI_RING_TYPE type, u32 chan, uintptr_t va_ptr) { - dma_addr_t phy_ptr; + dma_addr_t phy_ptr = 0; struct mhi_ring_ctxt *cs = &mhi_dev_ctxt->dev_space.ring_ctxt; switch (type) { diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c index 4563810d7e5b..9c35eeb177d9 100644 --- a/drivers/platform/msm/mhi_uci/mhi_uci.c +++ b/drivers/platform/msm/mhi_uci/mhi_uci.c @@ -326,73 +326,6 @@ static int mhi_init_inbound(struct uci_client *client_handle) return ret_val; } -static int mhi_uci_send_packet(struct mhi_client_handle **client_handle, - void *buf, - u32 size) -{ - u32 nr_avail_trbs = 0; - u32 i = 0; - void *data_loc = NULL; - unsigned long memcpy_result = 0; - int data_left_to_insert = 0; - size_t data_to_insert_now = 0; - u32 data_inserted_so_far = 0; - int ret_val = 0; - struct uci_client *uci_handle; - struct uci_buf *uci_buf; - - uci_handle = container_of(client_handle, struct uci_client, - out_attr.mhi_handle); - - nr_avail_trbs = atomic_read(&uci_handle->out_attr.avail_pkts); - data_left_to_insert = size; - - for (i = 0; i < nr_avail_trbs; ++i) { - data_to_insert_now = min_t(size_t, data_left_to_insert, - uci_handle->out_attr.max_packet_size); - data_loc = kmalloc(data_to_insert_now + sizeof(*uci_buf), - GFP_KERNEL); - if (!data_loc) { - uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE, - "Failed to allocate memory 0x%zx\n", - data_to_insert_now); - return -ENOMEM; - } - uci_buf = data_loc + data_to_insert_now; - uci_buf->data = data_loc; - uci_buf->pkt_id = uci_handle->out_attr.pkt_count++; - memcpy_result = copy_from_user(uci_buf->data, - buf + data_inserted_so_far, - data_to_insert_now); - if (memcpy_result) - goto error_xfer; - - uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE, - "At trb i = %d/%d, size = %lu, id %llu chan %d\n", - i, nr_avail_trbs, data_to_insert_now, uci_buf->pkt_id, - uci_handle->out_attr.chan_id); - ret_val = mhi_queue_xfer(*client_handle, uci_buf->data, - data_to_insert_now, MHI_EOT); - if (ret_val) { - goto error_xfer; - } else { - data_left_to_insert -= data_to_insert_now; - data_inserted_so_far += data_to_insert_now; - atomic_inc(&uci_handle->out_pkt_pend_ack); - atomic_dec(&uci_handle->out_attr.avail_pkts); - list_add_tail(&uci_buf->node, - &uci_handle->out_attr.buf_head); - } - if (!data_left_to_insert) - break; - } - return data_inserted_so_far; - -error_xfer: - kfree(uci_buf->data); - return data_inserted_so_far; -} - static int mhi_uci_send_status_cmd(struct uci_client *client) { void *buf = NULL; @@ -963,18 +896,11 @@ static ssize_t mhi_uci_client_write(struct file *file, goto sys_interrupt; } - while (bytes_transferrd != count) { - ret_val = mhi_uci_send_packet(&chan_attr->mhi_handle, - (void *)buf, count); - if (ret_val < 0) - goto sys_interrupt; + while (count) { + size_t xfer_size; + void *data_loc = NULL; + struct uci_buf *uci_buf; - bytes_transferrd += ret_val; - if (bytes_transferrd == count) - break; - uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE, - "No descriptors available, did we poll, chan %d?\n", - chan); mutex_unlock(&chan_attr->chan_lock); ret_val = wait_event_interruptible(chan_attr->wq, (atomic_read(&chan_attr->avail_pkts) || @@ -991,6 +917,37 @@ static ssize_t mhi_uci_client_write(struct file *file, ret_val = -ERESTARTSYS; goto sys_interrupt; } + + xfer_size = min_t(size_t, count, chan_attr->max_packet_size); + data_loc = kmalloc(xfer_size + sizeof(*uci_buf), GFP_KERNEL); + if (!data_loc) { + uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE, + "Failed to allocate memory %lu\n", xfer_size); + ret_val = -ENOMEM; + goto sys_interrupt; + } + + uci_buf = data_loc + xfer_size; + uci_buf->data = data_loc; + uci_buf->pkt_id = uci_handle->out_attr.pkt_count++; + ret_val = copy_from_user(uci_buf->data, buf, xfer_size); + if (unlikely(ret_val)) { + kfree(uci_buf->data); + goto sys_interrupt; + } + ret_val = mhi_queue_xfer(chan_attr->mhi_handle, uci_buf->data, + xfer_size, MHI_EOT); + if (unlikely(ret_val)) { + kfree(uci_buf->data); + goto sys_interrupt; + } + + bytes_transferrd += xfer_size; + count -= xfer_size; + buf += xfer_size; + atomic_inc(&uci_handle->out_pkt_pend_ack); + atomic_dec(&uci_handle->out_attr.avail_pkts); + list_add_tail(&uci_buf->node, &uci_handle->out_attr.buf_head); } mutex_unlock(&chan_attr->chan_lock); @@ -1073,7 +1030,7 @@ error_dts: static void process_rs232_state(struct uci_client *ctrl_client, struct mhi_result *result) { - struct rs232_ctrl_msg *rs232_pkt; + struct rs232_ctrl_msg *rs232_pkt = result->buf_addr; struct uci_client *client = NULL; struct mhi_uci_ctxt_t *uci_ctxt = ctrl_client->uci_ctxt; u32 msg_id; @@ -1094,7 +1051,6 @@ static void process_rs232_state(struct uci_client *ctrl_client, sizeof(struct rs232_ctrl_msg)); goto error_size; } - rs232_pkt = result->buf_addr; MHI_GET_CTRL_DEST_ID(CTRL_DEST_ID, rs232_pkt, chan); for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; i++) if (chan == uci_ctxt->client_handles[i].out_attr.chan_id || diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c index 9ea0b40304eb..0fec8acde96c 100644 --- a/drivers/platform/msm/qpnp-revid.c +++ b/drivers/platform/msm/qpnp-revid.c @@ -27,6 +27,7 @@ #define REVID_SUBTYPE 0x5 #define REVID_STATUS1 0x8 #define REVID_SPARE_0 0x60 +#define REVID_TP_REV 0xf1 #define REVID_FAB_ID 0xf2 #define QPNP_REVID_DEV_NAME "qcom,qpnp-revid" @@ -157,9 +158,9 @@ static size_t build_pmic_string(char *buf, size_t n, int sid, static int qpnp_revid_probe(struct platform_device *pdev) { u8 rev1, rev2, rev3, rev4, pmic_type, pmic_subtype, pmic_status; - u8 option1, option2, option3, option4, spare0, fab_id; + u8 option1, option2, option3, option4, spare0; unsigned int base; - int rc; + int rc, fab_id, tp_rev; char pmic_string[PMIC_STRING_MAXLENGTH] = {'\0'}; struct revid_chip *revid_chip; struct regmap *regmap; @@ -207,6 +208,11 @@ static int qpnp_revid_probe(struct platform_device *pdev) else fab_id = -EINVAL; + if (of_property_read_bool(pdev->dev.of_node, "qcom,tp-rev-valid")) + tp_rev = qpnp_read_byte(regmap, base + REVID_TP_REV); + else + tp_rev = -EINVAL; + revid_chip = devm_kzalloc(&pdev->dev, sizeof(struct revid_chip), GFP_KERNEL); if (!revid_chip) @@ -220,6 +226,7 @@ static int qpnp_revid_probe(struct platform_device *pdev) revid_chip->data.pmic_subtype = pmic_subtype; revid_chip->data.pmic_type = pmic_type; revid_chip->data.fab_id = fab_id; + revid_chip->data.tp_rev = tp_rev; if (pmic_subtype < ARRAY_SIZE(pmic_names)) revid_chip->data.pmic_name = pmic_names[pmic_subtype]; diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c index ddb2388c5006..e7710f929e71 100644 --- a/drivers/platform/msm/sps/sps.c +++ b/drivers/platform/msm/sps/sps.c @@ -2322,8 +2322,11 @@ int sps_deregister_bam_device(unsigned long dev_handle) mutex_lock(&bam->lock); sps_bam_device_de_init(bam); mutex_unlock(&bam->lock); + ipc_log_context_destroy(bam->ipc_log0); ipc_log_context_destroy(bam->ipc_log1); ipc_log_context_destroy(bam->ipc_log2); + ipc_log_context_destroy(bam->ipc_log3); + ipc_log_context_destroy(bam->ipc_log4); if (bam->props.virt_size) (void)iounmap(bam->props.virt_addr); diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c index a151d0c7a770..49e0666d0c23 100644 --- a/drivers/platform/msm/usb_bam.c +++ b/drivers/platform/msm/usb_bam.c @@ -103,7 +103,6 @@ struct usb_bam_sps_type { struct usb_bam_ctx_type { struct usb_bam_sps_type usb_bam_sps; struct resource *io_res; - void __iomem *regs; int irq; struct platform_device *usb_bam_pdev; struct workqueue_struct *usb_bam_wq; @@ -113,6 +112,7 @@ struct usb_bam_ctx_type { u32 inactivity_timer_ms; bool is_bam_inactivity; struct usb_bam_pipe_connect *usb_bam_connections; + struct msm_usb_bam_data *usb_bam_data; spinlock_t usb_bam_lock; }; @@ -235,10 +235,10 @@ void msm_bam_set_hsic_host_dev(struct device *dev) if (dev) { /* Hold the device until allowing lpm */ info[HSIC_CTRL].in_lpm = false; - log_event_dbg("%s: Getting hsic device %p\n", __func__, dev); + log_event_dbg("%s: Getting hsic device %pK\n", __func__, dev); pm_runtime_get(dev); } else if (host_info[HSIC_CTRL].dev) { - log_event_dbg("%s: Try Putting hsic device %p, lpm:%d\n", + log_event_dbg("%s: Try Putting hsic device %pK, lpm:%d\n", __func__, host_info[HSIC_CTRL].dev, info[HSIC_CTRL].in_lpm); /* Just release previous device if not already done */ @@ -320,8 +320,6 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) { int ret = 0; struct usb_bam_ctx_type *ctx = &msm_usb_bam[pipe_connect->bam_type]; - struct msm_usb_bam_platform_data *pdata = - ctx->usb_bam_pdev->dev.platform_data; struct sps_mem_buffer *data_buf = &(pipe_connect->data_mem_buf); struct sps_mem_buffer *desc_buf = &(pipe_connect->desc_mem_buf); @@ -360,7 +358,7 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) } data_buf->phys_base = pipe_connect->data_fifo_base_offset + - pdata->usb_bam_fifo_baseaddr; + ctx->usb_bam_data->usb_bam_fifo_baseaddr; data_buf->size = pipe_connect->data_fifo_size; data_buf->base = ioremap(data_buf->phys_base, data_buf->size); if (!data_buf->base) { @@ -372,7 +370,7 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) memset_io(data_buf->base, 0, data_buf->size); desc_buf->phys_base = pipe_connect->desc_fifo_base_offset + - pdata->usb_bam_fifo_baseaddr; + ctx->usb_bam_data->usb_bam_fifo_baseaddr; desc_buf->size = pipe_connect->desc_fifo_size; desc_buf->base = ioremap(desc_buf->phys_base, desc_buf->size); if (!desc_buf->base) { @@ -825,7 +823,7 @@ static bool _hsic_host_bam_resume_core(void) /* Exit from "full suspend" in case of hsic host */ if (host_info[HSIC_CTRL].dev && info[HSIC_CTRL].in_lpm) { - log_event_dbg("%s: Getting hsic device %p\n", __func__, + log_event_dbg("%s: Getting hsic device %pK\n", __func__, host_info[HSIC_CTRL].dev); pm_runtime_get(host_info[HSIC_CTRL].dev); info[HSIC_CTRL].in_lpm = false; @@ -839,7 +837,7 @@ static void _hsic_host_bam_suspend_core(void) log_event_dbg("%s: enter\n", __func__); if (host_info[HSIC_CTRL].dev && !info[HSIC_CTRL].in_lpm) { - log_event_dbg("%s: Putting hsic host device %p\n", __func__, + log_event_dbg("%s: Putting hsic host device %pK\n", __func__, host_info[HSIC_CTRL].dev); pm_runtime_put(host_info[HSIC_CTRL].dev); info[HSIC_CTRL].in_lpm = true; @@ -1070,7 +1068,6 @@ int usb_bam_connect(enum usb_ctrl cur_bam, int idx, u32 *bam_pipe_idx) struct usb_bam_pipe_connect *pipe_connect = &ctx->usb_bam_connections[idx]; struct device *bam_dev = &ctx->usb_bam_pdev->dev; - struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data; enum usb_bam_mode cur_mode; if (pipe_connect->enabled) { @@ -1096,7 +1093,7 @@ int usb_bam_connect(enum usb_ctrl cur_bam, int idx, u32 *bam_pipe_idx) spin_lock(&ctx->usb_bam_lock); /* Check if BAM requires RESET before connect and reset of first pipe */ - if ((pdata->reset_on_connect == true) && + if ((ctx->usb_bam_data->reset_on_connect == true) && (ctx->pipes_enabled_per_bam == 0)) { spin_unlock(&ctx->usb_bam_lock); @@ -1596,8 +1593,6 @@ static int ss_usb_cons_release_resource(void) static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam) { struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam]; - struct msm_usb_bam_platform_data *pdata = - ctx->usb_bam_pdev->dev.platform_data; struct ipa_rm_create_params usb_prod_create_params; struct ipa_rm_create_params usb_cons_create_params; int ret; @@ -1606,7 +1601,8 @@ static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam) memset(&usb_prod_create_params, 0, sizeof(usb_prod_create_params)); usb_prod_create_params.name = ipa_rm_resource_prod[cur_bam]; usb_prod_create_params.reg_params.notify_cb = usb_prod_notify_cb; - usb_prod_create_params.reg_params.user_data = &pdata->bam_type; + usb_prod_create_params.reg_params.user_data + = &ctx->usb_bam_data->bam_type; usb_prod_create_params.floor_voltage = IPA_VOLTAGE_SVS; ret = ipa_rm_create_resource(&usb_prod_create_params); if (ret) { @@ -1724,7 +1720,7 @@ static bool check_pipes_empty(enum usb_ctrl bam_type, u8 src_idx, u8 dst_idx) /* If we have any remaints in the pipes we don't go to sleep */ prod_pipe = ctx->usb_bam_sps.sps_pipes[src_idx]; cons_pipe = ctx->usb_bam_sps.sps_pipes[dst_idx]; - log_event_dbg("prod_pipe=%p, cons_pipe=%p\n", prod_pipe, cons_pipe); + log_event_dbg("prod_pipe=%pK, cons_pipe=%pK\n", prod_pipe, cons_pipe); if (!cons_pipe || (!prod_pipe && prod_pipe_connect->pipe_type == USB_BAM_PIPE_BAM2BAM)) { @@ -2091,7 +2087,7 @@ static bool msm_bam_host_lpm_ok(enum usb_ctrl bam_type) } /* HSIC host will go now to lpm */ - log_event_dbg("%s: vote for suspend hsic %p\n", + log_event_dbg("%s: vote for suspend hsic %pK\n", __func__, host_info[bam_type].dev); for (i = 0; i < ctx->max_connections; i++) { @@ -2136,16 +2132,14 @@ static int usb_bam_set_ipa_perf(enum usb_ctrl cur_bam, int ret; struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam]; struct ipa_rm_perf_profile ipa_rm_perf_prof; - struct msm_usb_bam_platform_data *pdata = - ctx->usb_bam_pdev->dev.platform_data; if (usb_connection_speed == USB_SPEED_SUPER) ipa_rm_perf_prof.max_supported_bandwidth_mbps = - pdata->max_mbps_superspeed; + ctx->usb_bam_data->max_mbps_superspeed; else /* Bam2Bam is supported only for SS and HS (HW limitation) */ ipa_rm_perf_prof.max_supported_bandwidth_mbps = - pdata->max_mbps_highspeed; + ctx->usb_bam_data->max_mbps_highspeed; /* * Having a max mbps property in dtsi file is a must @@ -2182,7 +2176,6 @@ int usb_bam_connect_ipa(enum usb_ctrl cur_bam, struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam]; struct usb_bam_pipe_connect *pipe_connect; struct device *bam_dev = &ctx->usb_bam_pdev->dev; - struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data; int ret; bool bam2bam, is_dpl; @@ -2260,7 +2253,8 @@ int usb_bam_connect_ipa(enum usb_ctrl cur_bam, /* Check if BAM requires RESET before connect and reset first pipe */ spin_lock(&ctx->usb_bam_lock); - if (pdata->reset_on_connect && !ctx->pipes_enabled_per_bam) { + if (ctx->usb_bam_data->reset_on_connect && + !ctx->pipes_enabled_per_bam) { spin_unlock(&ctx->usb_bam_lock); if (cur_bam == CI_CTRL) @@ -2454,7 +2448,7 @@ static void usb_bam_work(struct work_struct *w) if (pipe_iter->bam_type == pipe_connect->bam_type && pipe_iter->dir == PEER_PERIPHERAL_TO_USB && pipe_iter->enabled) { - log_event_dbg("%s: Register wakeup on pipe %p\n", + log_event_dbg("%s: Register wakeup on pipe %pK\n", __func__, pipe_iter); __usb_bam_register_wake_cb( pipe_connect->bam_type, i, @@ -2610,8 +2604,6 @@ int usb_bam_disconnect_pipe(enum usb_ctrl bam_type, u8 idx) struct usb_bam_pipe_connect *pipe_connect; struct device *bam_dev = &ctx->usb_bam_pdev->dev; int ret; - struct msm_usb_bam_platform_data *pdata = - ctx->usb_bam_pdev->dev.platform_data; pipe_connect = &ctx->usb_bam_connections[idx]; @@ -2639,7 +2631,8 @@ int usb_bam_disconnect_pipe(enum usb_ctrl bam_type, u8 idx) log_event_dbg("%s: success disconnecting pipe %d\n", __func__, idx); - if (pdata->reset_on_disconnect && !ctx->pipes_enabled_per_bam) { + if (ctx->usb_bam_data->reset_on_disconnect + && !ctx->pipes_enabled_per_bam) { if (bam_type == CI_CTRL) msm_hw_bam_disable(1); @@ -2807,10 +2800,10 @@ static void usb_bam_sps_events(enum sps_callback_case sps_cb_case, void *user) } } -static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( +static struct msm_usb_bam_data *usb_bam_dt_to_data( struct platform_device *pdev, u32 usb_addr) { - struct msm_usb_bam_platform_data *pdata; + struct msm_usb_bam_data *usb_bam_data; struct device_node *node = pdev->dev.of_node; int rc = 0; u8 i = 0; @@ -2819,10 +2812,10 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( u32 threshold, max_connections = 0; static struct usb_bam_pipe_connect *usb_bam_connections; - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { + usb_bam_data = devm_kzalloc(&pdev->dev, sizeof(*usb_bam_data), + GFP_KERNEL); + if (!usb_bam_data) return NULL; - } rc = of_property_read_u32(node, "qcom,bam-type", &bam); if (rc) { @@ -2835,7 +2828,7 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( __func__, bam); return NULL; } - pdata->bam_type = bam; + usb_bam_data->bam_type = bam; rc = of_property_read_u32(node, "qcom,bam-mode", &bam_mode); if (rc) { @@ -2845,49 +2838,49 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( bam_mode = USB_BAM_DEVICE; } - pdata->reset_on_connect = of_property_read_bool(node, + usb_bam_data->reset_on_connect = of_property_read_bool(node, "qcom,reset-bam-on-connect"); - pdata->reset_on_disconnect = of_property_read_bool(node, + usb_bam_data->reset_on_disconnect = of_property_read_bool(node, "qcom,reset-bam-on-disconnect"); rc = of_property_read_u32(node, "qcom,usb-bam-num-pipes", - &pdata->usb_bam_num_pipes); + &usb_bam_data->usb_bam_num_pipes); if (rc) { log_event_err("Invalid usb bam num pipes property\n"); return NULL; } rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-highspeed", - &pdata->max_mbps_highspeed); + &usb_bam_data->max_mbps_highspeed); if (rc) - pdata->max_mbps_highspeed = 0; + usb_bam_data->max_mbps_highspeed = 0; rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-superspeed", - &pdata->max_mbps_superspeed); + &usb_bam_data->max_mbps_superspeed); if (rc) - pdata->max_mbps_superspeed = 0; + usb_bam_data->max_mbps_superspeed = 0; rc = of_property_read_u32(node, "qcom,usb-bam-fifo-baseaddr", &addr); if (rc) pr_debug("%s: Invalid usb base address property\n", __func__); else - pdata->usb_bam_fifo_baseaddr = addr; + usb_bam_data->usb_bam_fifo_baseaddr = addr; - pdata->ignore_core_reset_ack = of_property_read_bool(node, + usb_bam_data->ignore_core_reset_ack = of_property_read_bool(node, "qcom,ignore-core-reset-ack"); - pdata->disable_clk_gating = of_property_read_bool(node, + usb_bam_data->disable_clk_gating = of_property_read_bool(node, "qcom,disable-clk-gating"); rc = of_property_read_u32(node, "qcom,usb-bam-override-threshold", &threshold); if (rc) - pdata->override_threshold = USB_THRESHOLD; + usb_bam_data->override_threshold = USB_THRESHOLD; else - pdata->override_threshold = threshold; + usb_bam_data->override_threshold = threshold; - pdata->enable_hsusb_bam_on_boot = of_property_read_bool(node, + usb_bam_data->enable_hsusb_bam_on_boot = of_property_read_bool(node, "qcom,enable-hsusb-bam-on-boot"); for_each_child_of_node(pdev->dev.of_node, node) @@ -2923,7 +2916,7 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( goto err; if (usb_bam_connections[i].mem_type == OCI_MEM) { - if (!pdata->usb_bam_fifo_baseaddr) { + if (!usb_bam_data->usb_bam_fifo_baseaddr) { log_event_err("%s: base address is missing\n", __func__); goto err; @@ -3007,7 +3000,7 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( msm_usb_bam[bam].usb_bam_connections = usb_bam_connections; msm_usb_bam[bam].max_connections = max_connections; - return pdata; + return usb_bam_data; err: log_event_err("%s: failed\n", __func__); return NULL; @@ -3016,9 +3009,8 @@ err: static int usb_bam_init(struct platform_device *pdev) { int ret; - struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data; - enum usb_ctrl bam_type = pdata->bam_type; - struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type]; + struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev); + enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type; struct sps_bam_props props; memset(&props, 0, sizeof(props)); @@ -3027,12 +3019,11 @@ static int usb_bam_init(struct platform_device *pdev) bam_enable_strings[bam_type]); props.phys_addr = ctx->io_res->start; - props.virt_addr = ctx->regs; props.virt_size = resource_size(ctx->io_res); props.irq = ctx->irq; - props.summing_threshold = pdata->override_threshold; - props.event_threshold = pdata->override_threshold; - props.num_pipes = pdata->usb_bam_num_pipes; + props.summing_threshold = ctx->usb_bam_data->override_threshold; + props.event_threshold = ctx->usb_bam_data->override_threshold; + props.num_pipes = ctx->usb_bam_data->usb_bam_num_pipes; props.callback = usb_bam_sps_events; props.user = bam_enable_strings[bam_type]; @@ -3040,10 +3031,10 @@ static int usb_bam_init(struct platform_device *pdev) * HSUSB and HSIC Cores don't support RESET ACK signal to BAMs * Hence, let BAM to ignore acknowledge from USB while resetting PIPE */ - if (pdata->ignore_core_reset_ack && bam_type != DWC3_CTRL) + if (ctx->usb_bam_data->ignore_core_reset_ack && bam_type != DWC3_CTRL) props.options = SPS_BAM_NO_EXT_P_RST; - if (pdata->disable_clk_gating) + if (ctx->usb_bam_data->disable_clk_gating) props.options |= SPS_BAM_NO_LOCAL_CLK_GATING; /* @@ -3051,7 +3042,8 @@ static int usb_bam_init(struct platform_device *pdev) * starting peripheral controller to avoid switching USB core mode * from legacy to BAM with ongoing data transfers. */ - if (pdata->enable_hsusb_bam_on_boot && bam_type == CI_CTRL) { + if (ctx->usb_bam_data->enable_hsusb_bam_on_boot + && bam_type == CI_CTRL) { pr_debug("Register and enable HSUSB BAM\n"); props.options |= SPS_BAM_OPT_ENABLE_AT_BOOT; } @@ -3068,9 +3060,8 @@ static int usb_bam_init(struct platform_device *pdev) static int enable_usb_bam(struct platform_device *pdev) { int ret; - struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data; - enum usb_ctrl bam_type = pdata->bam_type; - struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type]; + struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev); + enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type; ret = usb_bam_init(pdev); if (ret) { @@ -3137,14 +3128,19 @@ void usb_bam_register_panic_hdlr(void) &usb_bam_panic_blk); } +static void usb_bam_unregister_panic_hdlr(void) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, + &usb_bam_panic_blk); +} + static int usb_bam_probe(struct platform_device *pdev) { int ret, i, irq; struct resource *io_res; - void __iomem *regs; enum usb_ctrl bam_type; struct usb_bam_ctx_type *ctx; - struct msm_usb_bam_platform_data *pdata; + struct msm_usb_bam_data *usb_bam_data; dev_dbg(&pdev->dev, "usb_bam_probe\n"); @@ -3160,25 +3156,19 @@ static int usb_bam_probe(struct platform_device *pdev) return irq; } - regs = devm_ioremap(&pdev->dev, io_res->start, resource_size(io_res)); - if (!regs) { - log_event_err("%s: ioremap failed\n", __func__); - return -ENOMEM; - } - /* specify BAM physical address to be filled in BAM connections */ - pdata = usb_bam_dt_to_pdata(pdev, io_res->start); - if (!pdata) + usb_bam_data = usb_bam_dt_to_data(pdev, io_res->start); + if (!usb_bam_data) return -EINVAL; - pdev->dev.platform_data = pdata; - bam_type = pdata->bam_type; + bam_type = usb_bam_data->bam_type; ctx = &msm_usb_bam[bam_type]; + dev_set_drvdata(&pdev->dev, ctx); ctx->usb_bam_pdev = pdev; ctx->irq = irq; - ctx->regs = regs; ctx->io_res = io_res; + ctx->usb_bam_data = usb_bam_data; for (i = 0; i < ctx->max_connections; i++) { ctx->usb_bam_connections[i].enabled = 0; @@ -3319,15 +3309,15 @@ EXPORT_SYMBOL(usb_bam_get_bam_type); bool msm_usb_bam_enable(enum usb_ctrl bam, bool bam_enable) { - struct msm_usb_bam_platform_data *pdata; + struct msm_usb_bam_data *usb_bam_data; struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam]; if (!ctx->usb_bam_pdev) return 0; - pdata = ctx->usb_bam_pdev->dev.platform_data; + usb_bam_data = ctx->usb_bam_data; if ((bam != CI_CTRL) || !(bam_enable || - pdata->enable_hsusb_bam_on_boot)) + usb_bam_data->enable_hsusb_bam_on_boot)) return 0; msm_hw_bam_disable(1); @@ -3409,10 +3399,10 @@ EXPORT_SYMBOL(msm_bam_hsic_lpm_ok); static int usb_bam_remove(struct platform_device *pdev) { - struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data; - enum usb_ctrl bam_type = pdata->bam_type; - struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type]; + struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev); + usb_bam_unregister_panic_hdlr(); + sps_deregister_bam_device(ctx->h_bam); destroy_workqueue(ctx->usb_bam_wq); return 0; diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 0dd6017245fa..a45a51490817 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -297,6 +297,12 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(hw_current_max), POWER_SUPPLY_ATTR(real_type), POWER_SUPPLY_ATTR(pr_swap), + POWER_SUPPLY_ATTR(cc_step), + POWER_SUPPLY_ATTR(cc_step_sel), + POWER_SUPPLY_ATTR(sw_jeita_enabled), + POWER_SUPPLY_ATTR(pd_voltage_max), + POWER_SUPPLY_ATTR(pd_voltage_min), + POWER_SUPPLY_ATTR(sdp_current_max), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index de6c86984e3f..87ab2b24175f 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_SMB1351_USB_CHARGER) += battery.o smb1351-charger.o pmic-voter.o obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.o -obj-$(CONFIG_QPNP_SMB2) += battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o +obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_SMB138X_CHARGER) += battery.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_QPNP_QNOVO) += battery.o qpnp-qnovo.o diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 947108c1410e..88dcdd8fd7be 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -49,7 +49,7 @@ #define SRAM_READ "fg_sram_read" #define SRAM_WRITE "fg_sram_write" #define PROFILE_LOAD "fg_profile_load" -#define DELTA_SOC "fg_delta_soc" +#define TTF_PRIMING "fg_ttf_priming" /* Delta BSOC irq votable reasons */ #define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq" @@ -81,6 +81,8 @@ #define BATT_THERM_NUM_COEFFS 3 +#define MAX_CC_STEPS 20 + /* Debug flag definitions */ enum fg_debug_flag { FG_IRQ = BIT(0), /* Show interrupts */ @@ -228,6 +230,11 @@ enum esr_timer_config { NUM_ESR_TIMERS, }; +enum ttf_mode { + TTF_MODE_NORMAL = 0, + TTF_MODE_QNOVO, +}; + /* DT parameters for FG device */ struct fg_dt_props { bool force_load_profile; @@ -309,16 +316,31 @@ struct fg_irq_info { }; struct fg_circ_buf { - int arr[20]; + int arr[10]; int size; int head; }; +struct fg_cc_step_data { + int arr[MAX_CC_STEPS]; + int sel; +}; + struct fg_pt { s32 x; s32 y; }; +struct ttf { + struct fg_circ_buf ibatt; + struct fg_circ_buf vbatt; + struct fg_cc_step_data cc_step; + struct mutex lock; + int mode; + int last_ttf; + s64 last_ms; +}; + static const struct fg_pt fg_ln_table[] = { { 1000, 0 }, { 2000, 693 }, @@ -358,6 +380,7 @@ struct fg_chip { struct power_supply *usb_psy; struct power_supply *dc_psy; struct power_supply *parallel_psy; + struct power_supply *pc_port_psy; struct iio_channel *batt_id_chan; struct iio_channel *die_temp_chan; struct fg_memif *sram; @@ -374,9 +397,9 @@ struct fg_chip { struct fg_cyc_ctr_data cyc_ctr; struct notifier_block nb; struct fg_cap_learning cl; + struct ttf ttf; struct mutex bus_lock; struct mutex sram_rw_lock; - struct mutex batt_avg_lock; struct mutex charge_full_lock; u32 batt_soc_base; u32 batt_info_base; @@ -389,12 +412,14 @@ struct fg_chip { int prev_charge_status; int charge_done; int charge_type; + int online_status; int last_soc; int last_batt_temp; int health; int maint_soc; int delta_soc; int last_msoc; + int last_recharge_volt_mv; int esr_timer_charging_default[NUM_ESR_TIMERS]; enum slope_limit_status slope_limit_sts; bool profile_available; @@ -413,11 +438,8 @@ struct fg_chip { struct completion soc_ready; struct delayed_work profile_load_work; struct work_struct status_change_work; - struct work_struct cycle_count_work; - struct delayed_work batt_avg_work; + struct delayed_work ttf_work; struct delayed_work sram_dump_work; - struct fg_circ_buf ibatt_circ_buf; - struct fg_circ_buf vbatt_circ_buf; }; /* Debugfs data structures are below */ @@ -475,5 +497,6 @@ extern bool is_qnovo_en(struct fg_chip *chip); extern void fg_circ_buf_add(struct fg_circ_buf *, int); extern void fg_circ_buf_clr(struct fg_circ_buf *); extern int fg_circ_buf_avg(struct fg_circ_buf *, int *); +extern int fg_circ_buf_median(struct fg_circ_buf *, int *); extern int fg_lerp(const struct fg_pt *, size_t, s32, s32 *); #endif diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c index 9635044e02a5..0cb1dea7113b 100644 --- a/drivers/power/supply/qcom/fg-util.c +++ b/drivers/power/supply/qcom/fg-util.c @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ +#include <linux/sort.h> #include "fg-core.h" void fg_circ_buf_add(struct fg_circ_buf *buf, int val) @@ -39,6 +40,39 @@ int fg_circ_buf_avg(struct fg_circ_buf *buf, int *avg) return 0; } +static int cmp_int(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +int fg_circ_buf_median(struct fg_circ_buf *buf, int *median) +{ + int *temp; + + if (buf->size == 0) + return -ENODATA; + + if (buf->size == 1) { + *median = buf->arr[0]; + return 0; + } + + temp = kmalloc_array(buf->size, sizeof(*temp), GFP_KERNEL); + if (!temp) + return -ENOMEM; + + memcpy(temp, buf->arr, buf->size * sizeof(*temp)); + sort(temp, buf->size, sizeof(*temp), cmp_int, NULL); + + if (buf->size % 2) + *median = temp[buf->size / 2]; + else + *median = (temp[buf->size / 2 - 1] + temp[buf->size / 2]) / 2; + + kfree(temp); + return 0; +} + int fg_lerp(const struct fg_pt *pts, size_t tablesize, s32 input, s32 *output) { int i; diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c index b99558ed2100..3d0a71844608 100644 --- a/drivers/power/supply/qcom/pmic-voter.c +++ b/drivers/power/supply/qcom/pmic-voter.c @@ -438,12 +438,14 @@ out: int rerun_election(struct votable *votable) { int rc = 0; + int effective_result; lock_votable(votable); + effective_result = get_effective_result_locked(votable); if (votable->callback) rc = votable->callback(votable, - votable->data, - votable->effective_result, + votable->data, + effective_result, get_client_str(votable, votable->effective_client_id)); unlock_votable(votable); return rc; @@ -519,11 +521,10 @@ static int show_votable_clients(struct seq_file *m, void *data) lock_votable(votable); - seq_printf(m, "Votable %s:\n", votable->name); - seq_puts(m, "clients:\n"); for (i = 0; i < votable->num_clients; i++) { if (votable->client_strs[i]) { - seq_printf(m, "%-15s:\t\ten=%d\t\tv=%d\n", + seq_printf(m, "%s: %s:\t\t\ten=%d v=%d\n", + votable->name, votable->client_strs[i], votable->votes[i].enabled, votable->votes[i].value); @@ -542,11 +543,11 @@ static int show_votable_clients(struct seq_file *m, void *data) break; } - seq_printf(m, "type: %s\n", type_str); - seq_puts(m, "Effective:\n"); effective_client_str = get_effective_client_locked(votable); - seq_printf(m, "%-15s:\t\tv=%d\n", + seq_printf(m, "%s: effective=%s type=%s v=%d\n", + votable->name, effective_client_str ? effective_client_str : "none", + type_str, get_effective_result_locked(votable)); unlock_votable(votable); diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index e03681ec13cc..361efd4fbbbd 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -75,6 +75,8 @@ #define ESR_TIMER_CHG_MAX_OFFSET 0 #define ESR_TIMER_CHG_INIT_WORD 18 #define ESR_TIMER_CHG_INIT_OFFSET 2 +#define ESR_EXTRACTION_ENABLE_WORD 19 +#define ESR_EXTRACTION_ENABLE_OFFSET 0 #define PROFILE_LOAD_WORD 24 #define PROFILE_LOAD_OFFSET 0 #define ESR_RSLOW_DISCHG_WORD 34 @@ -1171,6 +1173,42 @@ static bool batt_psy_initialized(struct fg_chip *chip) return true; } +static bool usb_psy_initialized(struct fg_chip *chip) +{ + if (chip->usb_psy) + return true; + + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) + return false; + + return true; +} + +static bool pc_port_psy_initialized(struct fg_chip *chip) +{ + if (chip->pc_port_psy) + return true; + + chip->pc_port_psy = power_supply_get_by_name("pc_port"); + if (!chip->pc_port_psy) + return false; + + return true; +} + +static bool dc_psy_initialized(struct fg_chip *chip) +{ + if (chip->dc_psy) + return true; + + chip->dc_psy = power_supply_get_by_name("dc"); + if (!chip->dc_psy) + return false; + + return true; +} + static bool is_parallel_charger_available(struct fg_chip *chip) { if (!chip->parallel_psy) @@ -1277,11 +1315,20 @@ static bool is_temp_valid_cap_learning(struct fg_chip *chip) return true; } +#define QNOVO_CL_SKEW_DECIPCT -30 static void fg_cap_learning_post_process(struct fg_chip *chip) { int64_t max_inc_val, min_dec_val, old_cap; int rc; + if (is_qnovo_en(chip)) { + fg_dbg(chip, FG_CAP_LEARN, "applying skew %d on current learnt capacity %lld\n", + QNOVO_CL_SKEW_DECIPCT, chip->cl.final_cc_uah); + chip->cl.final_cc_uah = chip->cl.final_cc_uah * + (1000 + QNOVO_CL_SKEW_DECIPCT); + do_div(chip->cl.final_cc_uah, 1000); + } + max_inc_val = chip->cl.learned_cc_uah * (1000 + chip->dt.cl_max_cap_inc); do_div(max_inc_val, 1000); @@ -1590,6 +1637,9 @@ static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv) if (chip->wa_flags & PMI8998_V1_REV_WA) return 0; + if (voltage_mv == chip->last_recharge_volt_mv) + return 0; + fg_dbg(chip, FG_STATUS, "Setting recharge voltage to %dmV\n", voltage_mv); fg_encode(chip->sp, FG_SRAM_RECHARGE_VBATT_THR, voltage_mv, &buf); @@ -1604,6 +1654,7 @@ static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv) return rc; } + chip->last_recharge_volt_mv = voltage_mv; return 0; } @@ -1611,7 +1662,7 @@ static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv) static int fg_charge_full_update(struct fg_chip *chip) { union power_supply_propval prop = {0, }; - int rc, msoc, bsoc, recharge_soc; + int rc, msoc, bsoc, recharge_soc, msoc_raw; u8 full_soc[2] = {0xFF, 0xFF}; if (!chip->dt.hold_soc_while_full) @@ -1647,6 +1698,7 @@ static int fg_charge_full_update(struct fg_chip *chip) pr_err("Error in getting msoc, rc=%d\n", rc); goto out; } + msoc_raw = DIV_ROUND_CLOSEST(msoc * FULL_SOC_RAW, FULL_CAPACITY); fg_dbg(chip, FG_STATUS, "msoc: %d bsoc: %x health: %d status: %d full: %d\n", msoc, bsoc, chip->health, chip->charge_status, @@ -1670,7 +1722,7 @@ static int fg_charge_full_update(struct fg_chip *chip) fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n", msoc); } - } else if ((bsoc >> 8) <= recharge_soc && chip->charge_full) { + } else if (msoc_raw < recharge_soc && chip->charge_full) { chip->delta_soc = FULL_CAPACITY - msoc; /* @@ -1700,8 +1752,8 @@ static int fg_charge_full_update(struct fg_chip *chip) rc); goto out; } - fg_dbg(chip, FG_STATUS, "bsoc: %d recharge_soc: %d delta_soc: %d\n", - bsoc >> 8, recharge_soc, chip->delta_soc); + fg_dbg(chip, FG_STATUS, "msoc_raw = %d bsoc: %d recharge_soc: %d delta_soc: %d\n", + msoc_raw, bsoc >> 8, recharge_soc, chip->delta_soc); } else { goto out; } @@ -1913,6 +1965,33 @@ static int fg_adjust_recharge_soc(struct fg_chip *chip) return 0; } +static int fg_adjust_recharge_voltage(struct fg_chip *chip) +{ + int rc, recharge_volt_mv; + + if (chip->dt.auto_recharge_soc) + return 0; + + fg_dbg(chip, FG_STATUS, "health: %d chg_status: %d chg_done: %d\n", + chip->health, chip->charge_status, chip->charge_done); + + recharge_volt_mv = chip->dt.recharge_volt_thr_mv; + + /* Lower the recharge voltage in soft JEITA */ + if (chip->health == POWER_SUPPLY_HEALTH_WARM || + chip->health == POWER_SUPPLY_HEALTH_COOL) + recharge_volt_mv -= 200; + + rc = fg_set_recharge_voltage(chip, recharge_volt_mv); + if (rc < 0) { + pr_err("Error in setting recharge_voltage, rc=%d\n", + rc); + return rc; + } + + return 0; +} + static int fg_slope_limit_config(struct fg_chip *chip, int batt_temp) { enum slope_limit_status status; @@ -2130,102 +2209,67 @@ static int fg_esr_timer_config(struct fg_chip *chip, bool sleep) return 0; } -static void fg_batt_avg_update(struct fg_chip *chip) +static void fg_ttf_update(struct fg_chip *chip) { - if (chip->charge_status == chip->prev_charge_status) - return; - - cancel_delayed_work_sync(&chip->batt_avg_work); - fg_circ_buf_clr(&chip->ibatt_circ_buf); - fg_circ_buf_clr(&chip->vbatt_circ_buf); - - if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING || - chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) - schedule_delayed_work(&chip->batt_avg_work, - msecs_to_jiffies(2000)); -} - -static void status_change_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, status_change_work); + int rc; + int delay_ms; union power_supply_propval prop = {0, }; - int rc, batt_temp; + int online = 0; - if (!batt_psy_initialized(chip)) { - fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); - goto out; - } + if (usb_psy_initialized(chip)) { + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_ONLINE, &prop); + if (rc < 0) { + pr_err("Couldn't read usb ONLINE prop rc=%d\n", rc); + return; + } - rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, - &prop); - if (rc < 0) { - pr_err("Error in getting charging status, rc=%d\n", rc); - goto out; + online = online || prop.intval; } - chip->prev_charge_status = chip->charge_status; - chip->charge_status = prop.intval; - rc = power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_CHARGE_TYPE, &prop); - if (rc < 0) { - pr_err("Error in getting charge type, rc=%d\n", rc); - goto out; - } + if (pc_port_psy_initialized(chip)) { + rc = power_supply_get_property(chip->pc_port_psy, + POWER_SUPPLY_PROP_ONLINE, &prop); + if (rc < 0) { + pr_err("Couldn't read pc_port ONLINE prop rc=%d\n", rc); + return; + } - chip->charge_type = prop.intval; - rc = power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_CHARGE_DONE, &prop); - if (rc < 0) { - pr_err("Error in getting charge_done, rc=%d\n", rc); - goto out; + online = online || prop.intval; } - chip->charge_done = prop.intval; - if (chip->cyc_ctr.en) - schedule_work(&chip->cycle_count_work); - - fg_cap_learning_update(chip); - - rc = fg_charge_full_update(chip); - if (rc < 0) - pr_err("Error in charge_full_update, rc=%d\n", rc); - - rc = fg_adjust_recharge_soc(chip); - if (rc < 0) - pr_err("Error in adjusting recharge_soc, rc=%d\n", rc); - - rc = fg_adjust_ki_coeff_dischg(chip); - if (rc < 0) - pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc); - - rc = fg_esr_fcc_config(chip); - if (rc < 0) - pr_err("Error in adjusting FCC for ESR, rc=%d\n", rc); - - rc = fg_esr_timer_config(chip, false); - if (rc < 0) - pr_err("Error in configuring ESR timer, rc=%d\n", rc); - - rc = fg_get_battery_temp(chip, &batt_temp); - if (!rc) { - rc = fg_slope_limit_config(chip, batt_temp); - if (rc < 0) - pr_err("Error in configuring slope limiter rc:%d\n", - rc); + if (dc_psy_initialized(chip)) { + rc = power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_ONLINE, &prop); + if (rc < 0) { + pr_err("Couldn't read dc ONLINE prop rc=%d\n", rc); + return; + } - rc = fg_adjust_ki_coeff_full_soc(chip, batt_temp); - if (rc < 0) - pr_err("Error in configuring ki_coeff_full_soc rc:%d\n", - rc); + online = online || prop.intval; } - fg_batt_avg_update(chip); -out: - fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n", - chip->charge_status, chip->charge_type, chip->charge_done); - pm_relax(chip->dev); + if (chip->online_status == online) + return; + + chip->online_status = online; + if (online) + /* wait 35 seconds for the input to settle */ + delay_ms = 35000; + else + /* wait 5 seconds for current to settle during discharge */ + delay_ms = 5000; + + vote(chip->awake_votable, TTF_PRIMING, true, 0); + cancel_delayed_work_sync(&chip->ttf_work); + mutex_lock(&chip->ttf.lock); + fg_circ_buf_clr(&chip->ttf.ibatt); + fg_circ_buf_clr(&chip->ttf.vbatt); + chip->ttf.last_ttf = 0; + chip->ttf.last_ms = 0; + mutex_unlock(&chip->ttf.lock); + schedule_delayed_work(&chip->ttf_work, msecs_to_jiffies(delay_ms)); } static void restore_cycle_counter(struct fg_chip *chip) @@ -2233,6 +2277,9 @@ static void restore_cycle_counter(struct fg_chip *chip) int rc = 0, i; u8 data[2]; + if (!chip->cyc_ctr.en) + return; + mutex_lock(&chip->cyc_ctr.lock); for (i = 0; i < BUCKET_COUNT; i++) { rc = fg_sram_read(chip, CYCLE_COUNT_WORD + (i / 2), @@ -2286,20 +2333,25 @@ static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket) rc = fg_sram_write(chip, CYCLE_COUNT_WORD + (bucket / 2), CYCLE_COUNT_OFFSET + (bucket % 2) * 2, data, 2, FG_IMA_DEFAULT); - if (rc < 0) + if (rc < 0) { pr_err("failed to write BATT_CYCLE[%d] rc=%d\n", bucket, rc); - else - chip->cyc_ctr.count[bucket] = cyc_count; + return rc; + } + + chip->cyc_ctr.count[bucket] = cyc_count; + fg_dbg(chip, FG_STATUS, "Stored count %d in bucket %d\n", cyc_count, + bucket); + return rc; } -static void cycle_count_work(struct work_struct *work) +static void fg_cycle_counter_update(struct fg_chip *chip) { int rc = 0, bucket, i, batt_soc; - struct fg_chip *chip = container_of(work, - struct fg_chip, - cycle_count_work); + + if (!chip->cyc_ctr.en) + return; mutex_lock(&chip->cyc_ctr.lock); rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &batt_soc); @@ -2311,45 +2363,30 @@ static void cycle_count_work(struct work_struct *work) /* We need only the most significant byte here */ batt_soc = (u32)batt_soc >> 24; - if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) { - /* Find out which bucket the SOC falls in */ - bucket = batt_soc / BUCKET_SOC_PCT; - pr_debug("batt_soc: %d bucket: %d\n", batt_soc, bucket); + /* Find out which bucket the SOC falls in */ + bucket = batt_soc / BUCKET_SOC_PCT; - /* - * If we've started counting for the previous bucket, - * then store the counter for that bucket if the - * counter for current bucket is getting started. - */ - if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] && - !chip->cyc_ctr.started[bucket]) { - rc = fg_inc_store_cycle_ctr(chip, bucket - 1); - if (rc < 0) { - pr_err("Error in storing cycle_ctr rc: %d\n", - rc); - goto out; - } else { - chip->cyc_ctr.started[bucket - 1] = false; - chip->cyc_ctr.last_soc[bucket - 1] = 0; - } - } + if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) { if (!chip->cyc_ctr.started[bucket]) { chip->cyc_ctr.started[bucket] = true; chip->cyc_ctr.last_soc[bucket] = batt_soc; } - } else { + } else if (chip->charge_done || !is_input_present(chip)) { for (i = 0; i < BUCKET_COUNT; i++) { if (chip->cyc_ctr.started[i] && - batt_soc > chip->cyc_ctr.last_soc[i]) { + batt_soc > chip->cyc_ctr.last_soc[i] + 2) { rc = fg_inc_store_cycle_ctr(chip, i); if (rc < 0) pr_err("Error in storing cycle_ctr rc: %d\n", rc); chip->cyc_ctr.last_soc[i] = 0; + chip->cyc_ctr.started[i] = false; } - chip->cyc_ctr.started[i] = false; } } + + fg_dbg(chip, FG_STATUS, "batt_soc: %d bucket: %d chg_status: %d\n", + batt_soc, bucket, chip->charge_status); out: mutex_unlock(&chip->cyc_ctr.lock); } @@ -2370,6 +2407,87 @@ static int fg_get_cycle_count(struct fg_chip *chip) return count; } +static void status_change_work(struct work_struct *work) +{ + struct fg_chip *chip = container_of(work, + struct fg_chip, status_change_work); + union power_supply_propval prop = {0, }; + int rc, batt_temp; + + if (!batt_psy_initialized(chip)) { + fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); + goto out; + } + + rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, + &prop); + if (rc < 0) { + pr_err("Error in getting charging status, rc=%d\n", rc); + goto out; + } + + chip->prev_charge_status = chip->charge_status; + chip->charge_status = prop.intval; + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, &prop); + if (rc < 0) { + pr_err("Error in getting charge type, rc=%d\n", rc); + goto out; + } + + chip->charge_type = prop.intval; + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_DONE, &prop); + if (rc < 0) { + pr_err("Error in getting charge_done, rc=%d\n", rc); + goto out; + } + + chip->charge_done = prop.intval; + fg_cycle_counter_update(chip); + fg_cap_learning_update(chip); + + rc = fg_charge_full_update(chip); + if (rc < 0) + pr_err("Error in charge_full_update, rc=%d\n", rc); + + rc = fg_adjust_recharge_soc(chip); + if (rc < 0) + pr_err("Error in adjusting recharge_soc, rc=%d\n", rc); + + rc = fg_adjust_recharge_voltage(chip); + if (rc < 0) + pr_err("Error in adjusting recharge_voltage, rc=%d\n", rc); + + rc = fg_adjust_ki_coeff_dischg(chip); + if (rc < 0) + pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc); + + rc = fg_esr_fcc_config(chip); + if (rc < 0) + pr_err("Error in adjusting FCC for ESR, rc=%d\n", rc); + + rc = fg_get_battery_temp(chip, &batt_temp); + if (!rc) { + rc = fg_slope_limit_config(chip, batt_temp); + if (rc < 0) + pr_err("Error in configuring slope limiter rc:%d\n", + rc); + + rc = fg_adjust_ki_coeff_full_soc(chip, batt_temp); + if (rc < 0) + pr_err("Error in configuring ki_coeff_full_soc rc:%d\n", + rc); + } + + fg_ttf_update(chip); + +out: + fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n", + chip->charge_status, chip->charge_type, chip->charge_done); + pm_relax(chip->dev); +} + static int fg_bp_params_config(struct fg_chip *chip) { int rc = 0; @@ -2730,45 +2848,19 @@ static struct kernel_param_ops fg_restart_ops = { module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644); -#define BATT_AVG_POLL_PERIOD_MS 10000 -static void batt_avg_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, struct fg_chip, - batt_avg_work.work); - int rc, ibatt_now, vbatt_now; - - mutex_lock(&chip->batt_avg_lock); - rc = fg_get_battery_current(chip, &ibatt_now); - if (rc < 0) { - pr_err("failed to get battery current, rc=%d\n", rc); - goto reschedule; - } - - rc = fg_get_battery_voltage(chip, &vbatt_now); - if (rc < 0) { - pr_err("failed to get battery voltage, rc=%d\n", rc); - goto reschedule; - } - - fg_circ_buf_add(&chip->ibatt_circ_buf, ibatt_now); - fg_circ_buf_add(&chip->vbatt_circ_buf, vbatt_now); - -reschedule: - mutex_unlock(&chip->batt_avg_lock); - schedule_delayed_work(&chip->batt_avg_work, - msecs_to_jiffies(BATT_AVG_POLL_PERIOD_MS)); -} - #define HOURS_TO_SECONDS 3600 #define OCV_SLOPE_UV 10869 #define MILLI_UNIT 1000 #define MICRO_UNIT 1000000 -static int fg_get_time_to_full(struct fg_chip *chip, int *val) +#define NANO_UNIT 1000000000 +static int fg_get_time_to_full_locked(struct fg_chip *chip, int *val) { - int rc, ibatt_avg, vbatt_avg, rbatt, msoc, ocv_cc2cv, full_soc, - act_cap_uah; - s32 i_cc2cv, soc_cc2cv, ln_val, centi_tau_scale; - s64 t_predicted_cc = 0, t_predicted_cv = 0; + int rc, ibatt_avg, vbatt_avg, rbatt, msoc, full_soc, act_cap_mah, + i_cc2cv, soc_cc2cv, tau, divisor, iterm, ttf_mode, + i, soc_per_step, msoc_this_step, msoc_next_step, + ibatt_this_step, t_predicted_this_step, ttf_slope, + t_predicted_cv, t_predicted = 0; + s64 delta_ms; if (chip->bp.float_volt_uv <= 0) { pr_err("battery profile is not loaded\n"); @@ -2787,48 +2879,53 @@ static int fg_get_time_to_full(struct fg_chip *chip, int *val) } fg_dbg(chip, FG_TTF, "msoc=%d\n", msoc); + /* the battery is considered full if the SOC is 100% */ if (msoc >= 100) { *val = 0; return 0; } - mutex_lock(&chip->batt_avg_lock); - rc = fg_circ_buf_avg(&chip->ibatt_circ_buf, &ibatt_avg); - if (rc < 0) { - /* try to get instantaneous current */ - rc = fg_get_battery_current(chip, &ibatt_avg); - if (rc < 0) { - mutex_unlock(&chip->batt_avg_lock); - pr_err("failed to get battery current, rc=%d\n", rc); - return rc; - } + if (is_qnovo_en(chip)) + ttf_mode = TTF_MODE_QNOVO; + else + ttf_mode = TTF_MODE_NORMAL; + + /* when switching TTF algorithms the TTF needs to be reset */ + if (chip->ttf.mode != ttf_mode) { + fg_circ_buf_clr(&chip->ttf.ibatt); + fg_circ_buf_clr(&chip->ttf.vbatt); + chip->ttf.last_ttf = 0; + chip->ttf.last_ms = 0; + chip->ttf.mode = ttf_mode; } - rc = fg_circ_buf_avg(&chip->vbatt_circ_buf, &vbatt_avg); + /* at least 10 samples are required to produce a stable IBATT */ + if (chip->ttf.ibatt.size < 10) { + *val = -1; + return 0; + } + + rc = fg_circ_buf_median(&chip->ttf.ibatt, &ibatt_avg); if (rc < 0) { - /* try to get instantaneous voltage */ - rc = fg_get_battery_voltage(chip, &vbatt_avg); - if (rc < 0) { - mutex_unlock(&chip->batt_avg_lock); - pr_err("failed to get battery voltage, rc=%d\n", rc); - return rc; - } + pr_err("failed to get IBATT AVG rc=%d\n", rc); + return rc; } - mutex_unlock(&chip->batt_avg_lock); - fg_dbg(chip, FG_TTF, "vbatt_avg=%d\n", vbatt_avg); + rc = fg_circ_buf_median(&chip->ttf.vbatt, &vbatt_avg); + if (rc < 0) { + pr_err("failed to get VBATT AVG rc=%d\n", rc); + return rc; + } - /* clamp ibatt_avg to -150mA */ - if (ibatt_avg > -150000) - ibatt_avg = -150000; - fg_dbg(chip, FG_TTF, "ibatt_avg=%d\n", ibatt_avg); + ibatt_avg = -ibatt_avg / MILLI_UNIT; + vbatt_avg /= MILLI_UNIT; - /* reverse polarity to be consistent with unsigned current settings */ - ibatt_avg = abs(ibatt_avg); + /* clamp ibatt_avg to iterm */ + if (ibatt_avg < abs(chip->dt.sys_term_curr_ma)) + ibatt_avg = abs(chip->dt.sys_term_curr_ma); - /* estimated battery current at the CC to CV transition */ - i_cc2cv = div_s64((s64)ibatt_avg * vbatt_avg, chip->bp.float_volt_uv); - fg_dbg(chip, FG_TTF, "i_cc2cv=%d\n", i_cc2cv); + fg_dbg(chip, FG_TTF, "ibatt_avg=%d\n", ibatt_avg); + fg_dbg(chip, FG_TTF, "vbatt_avg=%d\n", vbatt_avg); rc = fg_get_battery_resistance(chip, &rbatt); if (rc < 0) { @@ -2836,19 +2933,14 @@ static int fg_get_time_to_full(struct fg_chip *chip, int *val) return rc; } - /* clamp rbatt to 50mOhms */ - if (rbatt < 50000) - rbatt = 50000; - + rbatt /= MILLI_UNIT; fg_dbg(chip, FG_TTF, "rbatt=%d\n", rbatt); - rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_uah); + rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_mah); if (rc < 0) { pr_err("failed to get ACT_BATT_CAP rc=%d\n", rc); return rc; } - act_cap_uah *= MILLI_UNIT; - fg_dbg(chip, FG_TTF, "actual_capacity_uah=%d\n", act_cap_uah); rc = fg_get_sram_prop(chip, FG_SRAM_FULL_SOC, &full_soc); if (rc < 0) { @@ -2857,69 +2949,148 @@ static int fg_get_time_to_full(struct fg_chip *chip, int *val) } full_soc = DIV_ROUND_CLOSEST(((u16)full_soc >> 8) * FULL_CAPACITY, FULL_SOC_RAW); - fg_dbg(chip, FG_TTF, "full_soc=%d\n", full_soc); + act_cap_mah = full_soc * act_cap_mah / 100; + fg_dbg(chip, FG_TTF, "act_cap_mah=%d\n", act_cap_mah); + + /* estimated battery current at the CC to CV transition */ + switch (chip->ttf.mode) { + case TTF_MODE_NORMAL: + i_cc2cv = ibatt_avg * vbatt_avg / + max(MILLI_UNIT, chip->bp.float_volt_uv / MILLI_UNIT); + break; + case TTF_MODE_QNOVO: + i_cc2cv = min( + chip->ttf.cc_step.arr[MAX_CC_STEPS - 1] / MILLI_UNIT, + ibatt_avg * vbatt_avg / + max(MILLI_UNIT, chip->bp.float_volt_uv / MILLI_UNIT)); + break; + default: + pr_err("TTF mode %d is not supported\n", chip->ttf.mode); + break; + } + fg_dbg(chip, FG_TTF, "i_cc2cv=%d\n", i_cc2cv); /* if we are already in CV state then we can skip estimating CC */ if (chip->charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER) - goto skip_cc_estimate; + goto cv_estimate; - /* if the charger is current limited then use power approximation */ - if (ibatt_avg > chip->bp.fastchg_curr_ma * MILLI_UNIT - 50000) - ocv_cc2cv = div_s64((s64)rbatt * ibatt_avg, MICRO_UNIT); - else - ocv_cc2cv = div_s64((s64)rbatt * i_cc2cv, MICRO_UNIT); - ocv_cc2cv = chip->bp.float_volt_uv - ocv_cc2cv; - fg_dbg(chip, FG_TTF, "ocv_cc2cv=%d\n", ocv_cc2cv); - - soc_cc2cv = div_s64(chip->bp.float_volt_uv - ocv_cc2cv, OCV_SLOPE_UV); /* estimated SOC at the CC to CV transition */ + soc_cc2cv = DIV_ROUND_CLOSEST(rbatt * i_cc2cv, OCV_SLOPE_UV); soc_cc2cv = 100 - soc_cc2cv; fg_dbg(chip, FG_TTF, "soc_cc2cv=%d\n", soc_cc2cv); - /* the esimated SOC may be lower than the current SOC */ - if (soc_cc2cv - msoc <= 0) - goto skip_cc_estimate; + switch (chip->ttf.mode) { + case TTF_MODE_NORMAL: + if (soc_cc2cv - msoc <= 0) + goto cv_estimate; + + divisor = max(100, (ibatt_avg + i_cc2cv) / 2 * 100); + t_predicted = div_s64((s64)act_cap_mah * (soc_cc2cv - msoc) * + HOURS_TO_SECONDS, divisor); + break; + case TTF_MODE_QNOVO: + soc_per_step = 100 / MAX_CC_STEPS; + for (i = msoc / soc_per_step; i < MAX_CC_STEPS - 1; ++i) { + msoc_next_step = (i + 1) * soc_per_step; + if (i == msoc / soc_per_step) + msoc_this_step = msoc; + else + msoc_this_step = i * soc_per_step; + + /* scale ibatt by 85% to account for discharge pulses */ + ibatt_this_step = min( + chip->ttf.cc_step.arr[i] / MILLI_UNIT, + ibatt_avg) * 85 / 100; + divisor = max(100, ibatt_this_step * 100); + t_predicted_this_step = div_s64((s64)act_cap_mah * + (msoc_next_step - msoc_this_step) * + HOURS_TO_SECONDS, divisor); + t_predicted += t_predicted_this_step; + fg_dbg(chip, FG_TTF, "[%d, %d] ma=%d t=%d\n", + msoc_this_step, msoc_next_step, + ibatt_this_step, t_predicted_this_step); + } + break; + default: + pr_err("TTF mode %d is not supported\n", chip->ttf.mode); + break; + } - t_predicted_cc = div_s64((s64)full_soc * act_cap_uah, 100); - t_predicted_cc = div_s64(t_predicted_cc * (soc_cc2cv - msoc), 100); - t_predicted_cc *= HOURS_TO_SECONDS; - t_predicted_cc = div_s64(t_predicted_cc, (ibatt_avg + i_cc2cv) / 2); +cv_estimate: + fg_dbg(chip, FG_TTF, "t_predicted_cc=%d\n", t_predicted); -skip_cc_estimate: - fg_dbg(chip, FG_TTF, "t_predicted_cc=%lld\n", t_predicted_cc); + iterm = max(100, abs(chip->dt.sys_term_curr_ma) + 200); + fg_dbg(chip, FG_TTF, "iterm=%d\n", iterm); - /* CV estimate starts here */ - if (chip->charge_type >= POWER_SUPPLY_CHARGE_TYPE_TAPER) - ln_val = ibatt_avg / (abs(chip->dt.sys_term_curr_ma) + 200); + if (chip->charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER) + tau = max(MILLI_UNIT, ibatt_avg * MILLI_UNIT / iterm); else - ln_val = i_cc2cv / (abs(chip->dt.sys_term_curr_ma) + 200); + tau = max(MILLI_UNIT, i_cc2cv * MILLI_UNIT / iterm); - if (msoc < 95) - centi_tau_scale = 100; - else - centi_tau_scale = 20 * (100 - msoc); - - fg_dbg(chip, FG_TTF, "ln_in=%d\n", ln_val); - rc = fg_lerp(fg_ln_table, ARRAY_SIZE(fg_ln_table), ln_val, &ln_val); - fg_dbg(chip, FG_TTF, "ln_out=%d\n", ln_val); - t_predicted_cv = div_s64((s64)act_cap_uah * rbatt, MICRO_UNIT); - t_predicted_cv = div_s64(t_predicted_cv * centi_tau_scale, 100); - t_predicted_cv = div_s64(t_predicted_cv * ln_val, MILLI_UNIT); - t_predicted_cv = div_s64(t_predicted_cv * HOURS_TO_SECONDS, MICRO_UNIT); - fg_dbg(chip, FG_TTF, "t_predicted_cv=%lld\n", t_predicted_cv); - *val = t_predicted_cc + t_predicted_cv; + rc = fg_lerp(fg_ln_table, ARRAY_SIZE(fg_ln_table), tau, &tau); + if (rc < 0) { + pr_err("failed to interpolate tau rc=%d\n", rc); + return rc; + } + + /* tau is scaled linearly from 95% to 100% SOC */ + if (msoc >= 95) + tau = tau * 2 * (100 - msoc) / 10; + + fg_dbg(chip, FG_TTF, "tau=%d\n", tau); + t_predicted_cv = div_s64((s64)act_cap_mah * rbatt * tau * + HOURS_TO_SECONDS, NANO_UNIT); + fg_dbg(chip, FG_TTF, "t_predicted_cv=%d\n", t_predicted_cv); + t_predicted += t_predicted_cv; + + fg_dbg(chip, FG_TTF, "t_predicted_prefilter=%d\n", t_predicted); + if (chip->ttf.last_ms != 0) { + delta_ms = ktime_ms_delta(ktime_get_boottime(), + ms_to_ktime(chip->ttf.last_ms)); + if (delta_ms > 10000) { + ttf_slope = div64_s64( + (s64)(t_predicted - chip->ttf.last_ttf) * + MICRO_UNIT, delta_ms); + if (ttf_slope > -100) + ttf_slope = -100; + else if (ttf_slope < -2000) + ttf_slope = -2000; + + t_predicted = div_s64( + (s64)ttf_slope * delta_ms, MICRO_UNIT) + + chip->ttf.last_ttf; + fg_dbg(chip, FG_TTF, "ttf_slope=%d\n", ttf_slope); + } else { + t_predicted = chip->ttf.last_ttf; + } + } + + /* clamp the ttf to 0 */ + if (t_predicted < 0) + t_predicted = 0; + + fg_dbg(chip, FG_TTF, "t_predicted_postfilter=%d\n", t_predicted); + *val = t_predicted; return 0; } +static int fg_get_time_to_full(struct fg_chip *chip, int *val) +{ + int rc; + + mutex_lock(&chip->ttf.lock); + rc = fg_get_time_to_full_locked(chip, val); + mutex_unlock(&chip->ttf.lock); + return rc; +} + #define CENTI_ICORRECT_C0 105 #define CENTI_ICORRECT_C1 20 static int fg_get_time_to_empty(struct fg_chip *chip, int *val) { - int rc, ibatt_avg, msoc, act_cap_uah; - s32 divisor; - s64 t_predicted; + int rc, ibatt_avg, msoc, full_soc, act_cap_mah, divisor; - rc = fg_circ_buf_avg(&chip->ibatt_circ_buf, &ibatt_avg); + rc = fg_circ_buf_median(&chip->ttf.ibatt, &ibatt_avg); if (rc < 0) { /* try to get instantaneous current */ rc = fg_get_battery_current(chip, &ibatt_avg); @@ -2929,31 +3100,36 @@ static int fg_get_time_to_empty(struct fg_chip *chip, int *val) } } - /* clamp ibatt_avg to 150mA */ - if (ibatt_avg < 150000) - ibatt_avg = 150000; + ibatt_avg /= MILLI_UNIT; + /* clamp ibatt_avg to 100mA */ + if (ibatt_avg < 100) + ibatt_avg = 100; - rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_uah); + rc = fg_get_prop_capacity(chip, &msoc); + if (rc < 0) { + pr_err("Error in getting capacity, rc=%d\n", rc); + return rc; + } + + rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_mah); if (rc < 0) { pr_err("Error in getting ACT_BATT_CAP, rc=%d\n", rc); return rc; } - act_cap_uah *= MILLI_UNIT; - rc = fg_get_prop_capacity(chip, &msoc); + rc = fg_get_sram_prop(chip, FG_SRAM_FULL_SOC, &full_soc); if (rc < 0) { - pr_err("Error in getting capacity, rc=%d\n", rc); + pr_err("failed to get full soc rc=%d\n", rc); return rc; } + full_soc = DIV_ROUND_CLOSEST(((u16)full_soc >> 8) * FULL_CAPACITY, + FULL_SOC_RAW); + act_cap_mah = full_soc * act_cap_mah / 100; - t_predicted = div_s64((s64)msoc * act_cap_uah, 100); - t_predicted *= HOURS_TO_SECONDS; divisor = CENTI_ICORRECT_C0 * 100 + CENTI_ICORRECT_C1 * msoc; - divisor = div_s64((s64)divisor * ibatt_avg, 10000); - if (divisor > 0) - t_predicted = div_s64(t_predicted, divisor); - - *val = t_predicted; + divisor = ibatt_avg * divisor / 100; + divisor = max(100, divisor); + *val = act_cap_mah * msoc * HOURS_TO_SECONDS / divisor; return 0; } @@ -3032,6 +3208,150 @@ static int fg_esr_validate(struct fg_chip *chip) return 0; } +static int fg_force_esr_meas(struct fg_chip *chip) +{ + int rc; + int esr_uohms; + + /* force esr extraction enable */ + rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, + ESR_EXTRACTION_ENABLE_OFFSET, BIT(0), BIT(0), + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to enable esr extn rc=%d\n", rc); + return rc; + } + + rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip), + LD_REG_CTRL_BIT, 0); + if (rc < 0) { + pr_err("Error in configuring qnovo_cfg rc=%d\n", rc); + return rc; + } + + rc = fg_masked_write(chip, BATT_INFO_TM_MISC1(chip), + ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT, + ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT); + if (rc < 0) { + pr_err("Error in configuring force ESR rc=%d\n", rc); + return rc; + } + + /* wait 1.5 seconds for hw to measure ESR */ + msleep(1500); + rc = fg_masked_write(chip, BATT_INFO_TM_MISC1(chip), + ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT, + 0); + if (rc < 0) { + pr_err("Error in restoring force ESR rc=%d\n", rc); + return rc; + } + + rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip), + LD_REG_CTRL_BIT, LD_REG_CTRL_BIT); + if (rc < 0) { + pr_err("Error in restoring qnovo_cfg rc=%d\n", rc); + return rc; + } + + /* force esr extraction disable */ + rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, + ESR_EXTRACTION_ENABLE_OFFSET, BIT(0), 0, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to disable esr extn rc=%d\n", rc); + return rc; + } + + fg_get_battery_resistance(chip, &esr_uohms); + fg_dbg(chip, FG_STATUS, "ESR uohms = %d\n", esr_uohms); + + return rc; +} + +static int fg_prepare_for_qnovo(struct fg_chip *chip, int qnovo_enable) +{ + int rc; + + /* force esr extraction disable when qnovo enables */ + rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, + ESR_EXTRACTION_ENABLE_OFFSET, + BIT(0), qnovo_enable ? 0 : BIT(0), + FG_IMA_DEFAULT); + if (rc < 0) + pr_err("Error in configuring esr extraction rc=%d\n", rc); + + rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip), + LD_REG_CTRL_BIT, + qnovo_enable ? LD_REG_CTRL_BIT : 0); + if (rc < 0) { + pr_err("Error in configuring qnovo_cfg rc=%d\n", rc); + return rc; + } + fg_dbg(chip, FG_STATUS, "Prepared for Qnovo\n"); + return 0; +} + +static void ttf_work(struct work_struct *work) +{ + struct fg_chip *chip = container_of(work, struct fg_chip, + ttf_work.work); + int rc, ibatt_now, vbatt_now, ttf; + ktime_t ktime_now; + + mutex_lock(&chip->ttf.lock); + if (chip->charge_status != POWER_SUPPLY_STATUS_CHARGING && + chip->charge_status != POWER_SUPPLY_STATUS_DISCHARGING) + goto end_work; + + rc = fg_get_battery_current(chip, &ibatt_now); + if (rc < 0) { + pr_err("failed to get battery current, rc=%d\n", rc); + goto end_work; + } + + rc = fg_get_battery_voltage(chip, &vbatt_now); + if (rc < 0) { + pr_err("failed to get battery voltage, rc=%d\n", rc); + goto end_work; + } + + fg_circ_buf_add(&chip->ttf.ibatt, ibatt_now); + fg_circ_buf_add(&chip->ttf.vbatt, vbatt_now); + + if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) { + rc = fg_get_time_to_full_locked(chip, &ttf); + if (rc < 0) { + pr_err("failed to get ttf, rc=%d\n", rc); + goto end_work; + } + + /* keep the wake lock and prime the IBATT and VBATT buffers */ + if (ttf < 0) { + /* delay for one FG cycle */ + schedule_delayed_work(&chip->ttf_work, + msecs_to_jiffies(1500)); + mutex_unlock(&chip->ttf.lock); + return; + } + + /* update the TTF reference point every minute */ + ktime_now = ktime_get_boottime(); + if (ktime_ms_delta(ktime_now, + ms_to_ktime(chip->ttf.last_ms)) > 60000 || + chip->ttf.last_ms == 0) { + chip->ttf.last_ttf = ttf; + chip->ttf.last_ms = ktime_to_ms(ktime_now); + } + } + + /* recurse every 10 seconds */ + schedule_delayed_work(&chip->ttf_work, msecs_to_jiffies(10000)); +end_work: + vote(chip->awake_votable, TTF_PRIMING, false, 0); + mutex_unlock(&chip->ttf.lock); +} + /* PSY CALLBACKS STAY HERE */ static int fg_psy_get_property(struct power_supply *psy, @@ -3108,6 +3428,20 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: rc = fg_get_sram_prop(chip, FG_SRAM_VBATT_FULL, &pval->intval); break; + case POWER_SUPPLY_PROP_CC_STEP: + if ((chip->ttf.cc_step.sel >= 0) && + (chip->ttf.cc_step.sel < MAX_CC_STEPS)) { + pval->intval = + chip->ttf.cc_step.arr[chip->ttf.cc_step.sel]; + } else { + pr_err("cc_step_sel is out of bounds [0, %d]\n", + chip->ttf.cc_step.sel); + return -EINVAL; + } + break; + case POWER_SUPPLY_PROP_CC_STEP_SEL: + pval->intval = chip->ttf.cc_step.sel; + break; default: pr_err("unsupported property %d\n", psp); rc = -EINVAL; @@ -3140,6 +3474,32 @@ static int fg_psy_set_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: rc = fg_set_constant_chg_voltage(chip, pval->intval); break; + case POWER_SUPPLY_PROP_RESISTANCE: + rc = fg_force_esr_meas(chip); + break; + case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE: + rc = fg_prepare_for_qnovo(chip, pval->intval); + break; + case POWER_SUPPLY_PROP_CC_STEP: + if ((chip->ttf.cc_step.sel >= 0) && + (chip->ttf.cc_step.sel < MAX_CC_STEPS)) { + chip->ttf.cc_step.arr[chip->ttf.cc_step.sel] = + pval->intval; + } else { + pr_err("cc_step_sel is out of bounds [0, %d]\n", + chip->ttf.cc_step.sel); + return -EINVAL; + } + break; + case POWER_SUPPLY_PROP_CC_STEP_SEL: + if ((pval->intval >= 0) && (pval->intval < MAX_CC_STEPS)) { + chip->ttf.cc_step.sel = pval->intval; + } else { + pr_err("cc_step_sel is out of bounds [0, %d]\n", + pval->intval); + return -EINVAL; + } + break; default: break; } @@ -3153,6 +3513,8 @@ static int fg_property_is_writeable(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CC_STEP: + case POWER_SUPPLY_PROP_CC_STEP_SEL: return 1; default: break; @@ -3213,6 +3575,8 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_SOC_REPORTING_READY, POWER_SUPPLY_PROP_DEBUG_BATTERY, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CC_STEP, + POWER_SUPPLY_PROP_CC_STEP_SEL, }; static const struct power_supply_desc fg_psy_desc = { @@ -3423,8 +3787,7 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } - if (chip->cyc_ctr.en) - restore_cycle_counter(chip); + restore_cycle_counter(chip); if (chip->dt.jeita_hyst_temp >= 0) { val = chip->dt.jeita_hyst_temp << JEITA_TEMP_HYST_SHIFT; @@ -3651,6 +4014,11 @@ static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data) if (rc < 0) pr_err("Error in adjusting timebase, rc=%d\n", rc); + rc = fg_adjust_recharge_voltage(chip); + if (rc < 0) + pr_err("Error in adjusting recharge_voltage, rc=%d\n", + rc); + chip->last_batt_temp = batt_temp; power_supply_changed(chip->batt_psy); } @@ -3698,8 +4066,7 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) int rc; fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); - if (chip->cyc_ctr.en) - schedule_work(&chip->cycle_count_work); + fg_cycle_counter_update(chip); if (chip->cl.active) fg_cap_learning_update(chip); @@ -4394,6 +4761,7 @@ static int fg_gen3_probe(struct platform_device *pdev) chip->charge_status = -EINVAL; chip->prev_charge_status = -EINVAL; chip->ki_coeff_full_soc = -EINVAL; + chip->online_status = -EINVAL; chip->regmap = dev_get_regmap(chip->dev->parent, NULL); if (!chip->regmap) { dev_err(chip->dev, "Parent regmap is unavailable\n"); @@ -4462,14 +4830,13 @@ static int fg_gen3_probe(struct platform_device *pdev) mutex_init(&chip->sram_rw_lock); mutex_init(&chip->cyc_ctr.lock); mutex_init(&chip->cl.lock); - mutex_init(&chip->batt_avg_lock); + mutex_init(&chip->ttf.lock); mutex_init(&chip->charge_full_lock); init_completion(&chip->soc_update); init_completion(&chip->soc_ready); INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work); INIT_WORK(&chip->status_change_work, status_change_work); - INIT_WORK(&chip->cycle_count_work, cycle_count_work); - INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work); + INIT_DELAYED_WORK(&chip->ttf_work, ttf_work); INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work); rc = fg_memif_init(chip); @@ -4566,7 +4933,7 @@ static int fg_gen3_suspend(struct device *dev) if (rc < 0) pr_err("Error in configuring ESR timer, rc=%d\n", rc); - cancel_delayed_work_sync(&chip->batt_avg_work); + cancel_delayed_work_sync(&chip->ttf_work); if (fg_sram_dump) cancel_delayed_work_sync(&chip->sram_dump_work); return 0; @@ -4581,9 +4948,7 @@ static int fg_gen3_resume(struct device *dev) if (rc < 0) pr_err("Error in configuring ESR timer, rc=%d\n", rc); - fg_circ_buf_clr(&chip->ibatt_circ_buf); - fg_circ_buf_clr(&chip->vbatt_circ_buf); - schedule_delayed_work(&chip->batt_avg_work, 0); + schedule_delayed_work(&chip->ttf_work, 0); if (fg_sram_dump) schedule_delayed_work(&chip->sram_dump_work, msecs_to_jiffies(fg_sram_dump_period_ms)); diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c index eb97eb0ff2ac..53af3415ec6a 100644 --- a/drivers/power/supply/qcom/qpnp-qnovo.c +++ b/drivers/power/supply/qcom/qpnp-qnovo.c @@ -20,6 +20,7 @@ #include <linux/of_irq.h> #include <linux/qpnp/qpnp-revid.h> #include <linux/pmic-voter.h> +#include <linux/delay.h> #define QNOVO_REVISION1 0x00 #define QNOVO_REVISION2 0x01 @@ -111,13 +112,26 @@ #define GAIN_LSB_FACTOR 976560 #define USER_VOTER "user_voter" +#define SHUTDOWN_VOTER "user_voter" #define OK_TO_QNOVO_VOTER "ok_to_qnovo_voter" #define QNOVO_VOTER "qnovo_voter" +#define FG_AVAILABLE_VOTER "FG_AVAILABLE_VOTER" +#define QNOVO_OVERALL_VOTER "QNOVO_OVERALL_VOTER" +#define QNI_PT_VOTER "QNI_PT_VOTER" +#define ESR_VOTER "ESR_VOTER" + +#define HW_OK_TO_QNOVO_VOTER "HW_OK_TO_QNOVO_VOTER" +#define CHG_READY_VOTER "CHG_READY_VOTER" +#define USB_READY_VOTER "USB_READY_VOTER" +#define DC_READY_VOTER "DC_READY_VOTER" + +#define PT_RESTART_VOTER "PT_RESTART_VOTER" struct qnovo_dt_props { bool external_rsense; struct device_node *revid_dev_node; + bool enable_for_dc; }; struct qnovo { @@ -127,6 +141,10 @@ struct qnovo { struct qnovo_dt_props dt; struct device *dev; struct votable *disable_votable; + struct votable *pt_dis_votable; + struct votable *not_ok_to_qnovo_votable; + struct votable *chg_ready_votable; + struct votable *awake_votable; struct class qnovo_class; struct pmic_revid_data *pmic_rev_id; u32 wa_flags; @@ -138,10 +156,18 @@ struct qnovo { s64 v_gain_mega; struct notifier_block nb; struct power_supply *batt_psy; + struct power_supply *bms_psy; + struct power_supply *usb_psy; + struct power_supply *dc_psy; struct work_struct status_change_work; int fv_uV_request; int fcc_uA_request; - bool ok_to_qnovo; + int usb_present; + int dc_present; + struct delayed_work usb_debounce_work; + struct delayed_work dc_debounce_work; + + struct delayed_work ptrain_restart_work; }; static int debug_mask; @@ -229,6 +255,39 @@ static bool is_batt_available(struct qnovo *chip) return true; } +static bool is_fg_available(struct qnovo *chip) +{ + if (!chip->bms_psy) + chip->bms_psy = power_supply_get_by_name("bms"); + + if (!chip->bms_psy) + return false; + + return true; +} + +static bool is_usb_available(struct qnovo *chip) +{ + if (!chip->usb_psy) + chip->usb_psy = power_supply_get_by_name("usb"); + + if (!chip->usb_psy) + return false; + + return true; +} + +static bool is_dc_available(struct qnovo *chip) +{ + if (!chip->dc_psy) + chip->dc_psy = power_supply_get_by_name("dc"); + + if (!chip->dc_psy) + return false; + + return true; +} + static int qnovo_batt_psy_update(struct qnovo *chip, bool disable) { union power_supply_propval pval = {0}; @@ -281,10 +340,86 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable, return -EINVAL; } + /* + * fg must be available for enable FG_AVAILABLE_VOTER + * won't enable it otherwise + */ + + if (is_fg_available(chip)) + power_supply_set_property(chip->bms_psy, + POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, + &pval); + + vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, disable, 0); rc = qnovo_batt_psy_update(chip, disable); return rc; } +static int pt_dis_votable_cb(struct votable *votable, void *data, int disable, + const char *client) +{ + struct qnovo *chip = data; + int rc; + + if (disable) { + cancel_delayed_work_sync(&chip->ptrain_restart_work); + vote(chip->awake_votable, PT_RESTART_VOTER, false, 0); + } + + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, + (bool)disable ? 0 : QNOVO_PTRAIN_EN_BIT); + if (rc < 0) { + dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n", + (bool)disable ? "disable" : "enable", rc); + return rc; + } + + if (!disable) { + vote(chip->awake_votable, PT_RESTART_VOTER, true, 0); + schedule_delayed_work(&chip->ptrain_restart_work, + msecs_to_jiffies(20)); + } + + return 0; +} + +static int not_ok_to_qnovo_cb(struct votable *votable, void *data, + int not_ok_to_qnovo, + const char *client) +{ + struct qnovo *chip = data; + + vote(chip->disable_votable, OK_TO_QNOVO_VOTER, not_ok_to_qnovo, 0); + if (not_ok_to_qnovo) + vote(chip->disable_votable, USER_VOTER, true, 0); + + kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); + return 0; +} + +static int chg_ready_cb(struct votable *votable, void *data, int ready, + const char *client) +{ + struct qnovo *chip = data; + + vote(chip->not_ok_to_qnovo_votable, CHG_READY_VOTER, !ready, 0); + + return 0; +} + +static int awake_cb(struct votable *votable, void *data, int awake, + const char *client) +{ + struct qnovo *chip = data; + + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); + + return 0; +} + static int qnovo_parse_dt(struct qnovo *chip) { struct device_node *node = chip->dev->of_node; @@ -309,6 +444,8 @@ static int qnovo_parse_dt(struct qnovo *chip) pr_err("Missing qcom,pmic-revid property - driver failed\n"); return -EINVAL; } + chip->dt.enable_for_dc = of_property_read_bool(node, + "qcom,enable-for-dc"); return 0; } @@ -626,8 +763,9 @@ static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr, char *buf) { struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + int val = get_effective_result(chip->not_ok_to_qnovo_votable); - return snprintf(buf, PAGE_SIZE, "%d\n", chip->ok_to_qnovo); + return snprintf(buf, PAGE_SIZE, "%d\n", !val); } static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr, @@ -656,21 +794,10 @@ static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr, static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr, char *ubuf) { - int i = attr - qnovo_attributes; struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); - u8 buf[2] = {0, 0}; - u16 regval; - int rc; + int val = get_effective_result(chip->pt_dis_votable); - rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs); - if (rc < 0) { - pr_err("Couldn't read %s rc = %d\n", params[i].name, rc); - return -EINVAL; - } - regval = buf[1] << 8 | buf[0]; - - return snprintf(ubuf, PAGE_SIZE, "%d\n", - (int)(regval & QNOVO_PTRAIN_EN_BIT)); + return snprintf(ubuf, PAGE_SIZE, "%d\n", !val); } static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr, @@ -678,21 +805,12 @@ static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr, { struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); unsigned long val; - int rc = 0; - - if (get_effective_result(chip->disable_votable)) - return -EINVAL; if (kstrtoul(ubuf, 0, &val)) return -EINVAL; - rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, - (bool)val ? QNOVO_PTRAIN_EN_BIT : 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n", - (bool)val ? "enable" : "disable", rc); - return rc; - } + /* val being 0, userspace wishes to disable pt so vote true */ + vote(chip->pt_dis_votable, QNI_PT_VOTER, val ? false : true, 0); return count; } @@ -1116,41 +1234,170 @@ static int qnovo_update_status(struct qnovo *chip) { u8 val = 0; int rc; - bool ok_to_qnovo; - bool changed = false; + bool hw_ok_to_qnovo; rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1); if (rc < 0) { pr_err("Couldn't read error sts rc = %d\n", rc); - ok_to_qnovo = false; + hw_ok_to_qnovo = false; } else { /* * For CV mode keep qnovo enabled, userspace is expected to * disable it after few runs */ - ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ? true : false; + hw_ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ? + true : false; } - if (chip->ok_to_qnovo ^ ok_to_qnovo) { + vote(chip->not_ok_to_qnovo_votable, HW_OK_TO_QNOVO_VOTER, + !hw_ok_to_qnovo, 0); + return 0; +} - vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !ok_to_qnovo, 0); - if (!ok_to_qnovo) - vote(chip->disable_votable, USER_VOTER, true, 0); +static void usb_debounce_work(struct work_struct *work) +{ + struct qnovo *chip = container_of(work, + struct qnovo, usb_debounce_work.work); - chip->ok_to_qnovo = ok_to_qnovo; - changed = true; - } + vote(chip->chg_ready_votable, USB_READY_VOTER, true, 0); + vote(chip->awake_votable, USB_READY_VOTER, false, 0); +} - return changed; +static void dc_debounce_work(struct work_struct *work) +{ + struct qnovo *chip = container_of(work, + struct qnovo, dc_debounce_work.work); + + vote(chip->chg_ready_votable, DC_READY_VOTER, true, 0); + vote(chip->awake_votable, DC_READY_VOTER, false, 0); } +#define DEBOUNCE_MS 15000 /* 15 seconds */ static void status_change_work(struct work_struct *work) { struct qnovo *chip = container_of(work, struct qnovo, status_change_work); + union power_supply_propval pval; + bool usb_present = false, dc_present = false; + int rc; + + if (is_fg_available(chip)) + vote(chip->disable_votable, FG_AVAILABLE_VOTER, false, 0); + + if (is_usb_available(chip)) { + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + usb_present = (rc < 0) ? 0 : pval.intval; + } + + if (chip->usb_present && !usb_present) { + /* removal */ + chip->usb_present = 0; + cancel_delayed_work_sync(&chip->usb_debounce_work); + vote(chip->awake_votable, USB_READY_VOTER, false, 0); + vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0); + } else if (!chip->usb_present && usb_present) { + /* insertion */ + chip->usb_present = 1; + vote(chip->awake_votable, USB_READY_VOTER, true, 0); + schedule_delayed_work(&chip->usb_debounce_work, + msecs_to_jiffies(DEBOUNCE_MS)); + } + + if (is_dc_available(chip)) { + rc = power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_PRESENT, + &pval); + dc_present = (rc < 0) ? 0 : pval.intval; + } - if (qnovo_update_status(chip)) - kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); + if (usb_present) + dc_present = 0; + + /* disable qnovo for dc path by forcing dc_present = 0 always */ + if (!chip->dt.enable_for_dc) + dc_present = 0; + + if (chip->dc_present && !dc_present) { + /* removal */ + chip->dc_present = 0; + cancel_delayed_work_sync(&chip->dc_debounce_work); + vote(chip->awake_votable, DC_READY_VOTER, false, 0); + vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0); + } else if (!chip->dc_present && dc_present) { + /* insertion */ + chip->dc_present = 1; + vote(chip->awake_votable, DC_READY_VOTER, true, 0); + schedule_delayed_work(&chip->dc_debounce_work, + msecs_to_jiffies(DEBOUNCE_MS)); + } + + qnovo_update_status(chip); +} + +static void ptrain_restart_work(struct work_struct *work) +{ + struct qnovo *chip = container_of(work, + struct qnovo, ptrain_restart_work.work); + u8 pt_t1, pt_t2; + int rc; + u8 pt_en; + + rc = qnovo_read(chip, QNOVO_PTRAIN_EN, &pt_en, 1); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read QNOVO_PTRAIN_EN rc = %d\n", + rc); + goto clean_up; + } + + if (!pt_en) { + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, + QNOVO_PTRAIN_EN_BIT, QNOVO_PTRAIN_EN_BIT); + if (rc < 0) { + dev_err(chip->dev, "Couldn't enable pulse train rc=%d\n", + rc); + goto clean_up; + } + /* sleep 20ms for the pulse trains to restart and settle */ + msleep(20); + } + + rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t1, 1); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n", + rc); + goto clean_up; + } + + /* pttime increments every 2 seconds */ + msleep(2100); + + rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t2, 1); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n", + rc); + goto clean_up; + } + + if (pt_t1 != pt_t2) + goto clean_up; + + /* Toggle pt enable to restart pulse train */ + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, 0); + if (rc < 0) { + dev_err(chip->dev, "Couldn't disable pulse train rc=%d\n", rc); + goto clean_up; + } + msleep(1000); + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, + QNOVO_PTRAIN_EN_BIT); + if (rc < 0) { + dev_err(chip->dev, "Couldn't enable pulse train rc=%d\n", rc); + goto clean_up; + } + +clean_up: + vote(chip->awake_votable, PT_RESTART_VOTER, false, 0); } static int qnovo_notifier_call(struct notifier_block *nb, @@ -1162,7 +1409,10 @@ static int qnovo_notifier_call(struct notifier_block *nb, if (ev != PSY_EVENT_PROP_CHANGED) return NOTIFY_OK; - if (strcmp(psy->desc->name, "battery") == 0) + if (strcmp(psy->desc->name, "battery") == 0 + || strcmp(psy->desc->name, "bms") == 0 + || strcmp(psy->desc->name, "usb") == 0 + || strcmp(psy->desc->name, "dc") == 0) schedule_work(&chip->status_change_work); return NOTIFY_OK; @@ -1171,8 +1421,27 @@ static int qnovo_notifier_call(struct notifier_block *nb, static irqreturn_t handle_ptrain_done(int irq, void *data) { struct qnovo *chip = data; + union power_supply_propval pval = {0}; qnovo_update_status(chip); + + /* + * hw resets pt_en bit once ptrain_done triggers. + * vote on behalf of QNI to disable it such that + * once QNI enables it, the votable state changes + * and the callback that sets it is indeed invoked + */ + vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0); + + vote(chip->pt_dis_votable, ESR_VOTER, true, 0); + if (is_fg_available(chip) + && !get_client_vote(chip->disable_votable, USER_VOTER) + && !get_effective_result(chip->not_ok_to_qnovo_votable)) + power_supply_set_property(chip->bms_psy, + POWER_SUPPLY_PROP_RESISTANCE, + &pval); + + vote(chip->pt_dis_votable, ESR_VOTER, false, 0); kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); return IRQ_HANDLED; } @@ -1185,7 +1454,15 @@ static int qnovo_hw_init(struct qnovo *chip) u8 vadc_offset, vadc_gain; u8 val; + vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0); + vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0); + vote(chip->disable_votable, USER_VOTER, true, 0); + vote(chip->disable_votable, FG_AVAILABLE_VOTER, true, 0); + + vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0); + vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, true, 0); + vote(chip->pt_dis_votable, ESR_VOTER, false, 0); val = 0; rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1); @@ -1349,12 +1626,45 @@ static int qnovo_probe(struct platform_device *pdev) goto cleanup; } + chip->pt_dis_votable = create_votable("QNOVO_PT_DIS", VOTE_SET_ANY, + pt_dis_votable_cb, chip); + if (IS_ERR(chip->pt_dis_votable)) { + rc = PTR_ERR(chip->pt_dis_votable); + goto destroy_disable_votable; + } + + chip->not_ok_to_qnovo_votable = create_votable("QNOVO_NOT_OK", + VOTE_SET_ANY, + not_ok_to_qnovo_cb, chip); + if (IS_ERR(chip->not_ok_to_qnovo_votable)) { + rc = PTR_ERR(chip->not_ok_to_qnovo_votable); + goto destroy_pt_dis_votable; + } + + chip->chg_ready_votable = create_votable("QNOVO_CHG_READY", + VOTE_SET_ANY, + chg_ready_cb, chip); + if (IS_ERR(chip->chg_ready_votable)) { + rc = PTR_ERR(chip->chg_ready_votable); + goto destroy_not_ok_to_qnovo_votable; + } + + chip->awake_votable = create_votable("QNOVO_AWAKE", VOTE_SET_ANY, + awake_cb, chip); + if (IS_ERR(chip->awake_votable)) { + rc = PTR_ERR(chip->awake_votable); + goto destroy_chg_ready_votable; + } + INIT_WORK(&chip->status_change_work, status_change_work); + INIT_DELAYED_WORK(&chip->dc_debounce_work, dc_debounce_work); + INIT_DELAYED_WORK(&chip->usb_debounce_work, usb_debounce_work); + INIT_DELAYED_WORK(&chip->ptrain_restart_work, ptrain_restart_work); rc = qnovo_hw_init(chip); if (rc < 0) { pr_err("Couldn't initialize hardware rc=%d\n", rc); - goto destroy_votable; + goto destroy_awake_votable; } rc = qnovo_register_notifier(chip); @@ -1390,7 +1700,15 @@ static int qnovo_probe(struct platform_device *pdev) unreg_notifier: power_supply_unreg_notifier(&chip->nb); -destroy_votable: +destroy_awake_votable: + destroy_votable(chip->awake_votable); +destroy_chg_ready_votable: + destroy_votable(chip->chg_ready_votable); +destroy_not_ok_to_qnovo_votable: + destroy_votable(chip->not_ok_to_qnovo_votable); +destroy_pt_dis_votable: + destroy_votable(chip->pt_dis_votable); +destroy_disable_votable: destroy_votable(chip->disable_votable); cleanup: platform_set_drvdata(pdev, NULL); @@ -1403,11 +1721,21 @@ static int qnovo_remove(struct platform_device *pdev) class_unregister(&chip->qnovo_class); power_supply_unreg_notifier(&chip->nb); + destroy_votable(chip->chg_ready_votable); + destroy_votable(chip->not_ok_to_qnovo_votable); + destroy_votable(chip->pt_dis_votable); destroy_votable(chip->disable_votable); platform_set_drvdata(pdev, NULL); return 0; } +static void qnovo_shutdown(struct platform_device *pdev) +{ + struct qnovo *chip = platform_get_drvdata(pdev); + + vote(chip->not_ok_to_qnovo_votable, SHUTDOWN_VOTER, true, 0); +} + static const struct of_device_id match_table[] = { { .compatible = "qcom,qpnp-qnovo", }, { }, @@ -1421,6 +1749,7 @@ static struct platform_driver qnovo_driver = { }, .probe = qnovo_probe, .remove = qnovo_remove, + .shutdown = qnovo_shutdown, }; module_platform_driver(qnovo_driver); diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 7b7f991ecba9..c085256a794a 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -19,6 +19,7 @@ #include <linux/power_supply.h> #include <linux/of.h> #include <linux/of_irq.h> +#include <linux/log2.h> #include <linux/qpnp/qpnp-revid.h> #include <linux/regulator/driver.h> #include <linux/regulator/of_regulator.h> @@ -122,87 +123,6 @@ static struct smb_params v1_params = { .max_u = 1575000, .step_u = 25000, }, - .step_soc_threshold[0] = { - .name = "step charge soc threshold 1", - .reg = STEP_CHG_SOC_OR_BATT_V_TH1_REG, - .min_u = 0, - .max_u = 100, - .step_u = 1, - }, - .step_soc_threshold[1] = { - .name = "step charge soc threshold 2", - .reg = STEP_CHG_SOC_OR_BATT_V_TH2_REG, - .min_u = 0, - .max_u = 100, - .step_u = 1, - }, - .step_soc_threshold[2] = { - .name = "step charge soc threshold 3", - .reg = STEP_CHG_SOC_OR_BATT_V_TH3_REG, - .min_u = 0, - .max_u = 100, - .step_u = 1, - }, - .step_soc_threshold[3] = { - .name = "step charge soc threshold 4", - .reg = STEP_CHG_SOC_OR_BATT_V_TH4_REG, - .min_u = 0, - .max_u = 100, - .step_u = 1, - }, - .step_soc = { - .name = "step charge soc", - .reg = STEP_CHG_SOC_VBATT_V_REG, - .min_u = 0, - .max_u = 100, - .step_u = 1, - .set_proc = smblib_mapping_soc_from_field_value, - }, - .step_cc_delta[0] = { - .name = "step charge current delta 1", - .reg = STEP_CHG_CURRENT_DELTA1_REG, - .min_u = 100000, - .max_u = 3200000, - .step_u = 100000, - .get_proc = smblib_mapping_cc_delta_to_field_value, - .set_proc = smblib_mapping_cc_delta_from_field_value, - }, - .step_cc_delta[1] = { - .name = "step charge current delta 2", - .reg = STEP_CHG_CURRENT_DELTA2_REG, - .min_u = 100000, - .max_u = 3200000, - .step_u = 100000, - .get_proc = smblib_mapping_cc_delta_to_field_value, - .set_proc = smblib_mapping_cc_delta_from_field_value, - }, - .step_cc_delta[2] = { - .name = "step charge current delta 3", - .reg = STEP_CHG_CURRENT_DELTA3_REG, - .min_u = 100000, - .max_u = 3200000, - .step_u = 100000, - .get_proc = smblib_mapping_cc_delta_to_field_value, - .set_proc = smblib_mapping_cc_delta_from_field_value, - }, - .step_cc_delta[3] = { - .name = "step charge current delta 4", - .reg = STEP_CHG_CURRENT_DELTA4_REG, - .min_u = 100000, - .max_u = 3200000, - .step_u = 100000, - .get_proc = smblib_mapping_cc_delta_to_field_value, - .set_proc = smblib_mapping_cc_delta_from_field_value, - }, - .step_cc_delta[4] = { - .name = "step charge current delta 5", - .reg = STEP_CHG_CURRENT_DELTA5_REG, - .min_u = 100000, - .max_u = 3200000, - .step_u = 100000, - .get_proc = smblib_mapping_cc_delta_to_field_value, - .set_proc = smblib_mapping_cc_delta_from_field_value, - }, .freq_buck = { .name = "buck switching frequency", .reg = CFG_BUCKBOOST_FREQ_SELECT_BUCK_REG, @@ -236,7 +156,6 @@ static struct smb_params pm660_params = { }, }; -#define STEP_CHARGING_MAX_STEPS 5 struct smb_dt_props { int usb_icl_ua; int dc_icl_ua; @@ -244,14 +163,13 @@ struct smb_dt_props { int wipower_max_uw; int min_freq_khz; int max_freq_khz; - u32 step_soc_threshold[STEP_CHARGING_MAX_STEPS - 1]; - s32 step_cc_delta[STEP_CHARGING_MAX_STEPS]; struct device_node *revid_dev_node; int float_option; int chg_inhibit_thr_mv; bool no_battery; bool hvdcp_disable; bool auto_recharge_soc; + int wd_bark_time; }; struct smb2 { @@ -273,6 +191,11 @@ module_param_named( #define MICRO_1P5A 1500000 #define MICRO_P1A 100000 #define OTG_DEFAULT_DEGLITCH_TIME_MS 50 +#define MIN_WD_BARK_TIME 16 +#define DEFAULT_WD_BARK_TIME 64 +#define BITE_WDOG_TIMEOUT_8S 0x3 +#define BARK_WDOG_TIMEOUT_MASK GENMASK(3, 2) +#define BARK_WDOG_TIMEOUT_SHIFT 2 static int smb2_parse_dt(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; @@ -284,27 +207,16 @@ static int smb2_parse_dt(struct smb2 *chip) return -EINVAL; } - chg->step_chg_enabled = true; + chg->step_chg_enabled = of_property_read_bool(node, + "qcom,step-charging-enable"); - if (of_property_count_u32_elems(node, "qcom,step-soc-thresholds") - != STEP_CHARGING_MAX_STEPS - 1) - chg->step_chg_enabled = false; + chg->sw_jeita_enabled = of_property_read_bool(node, + "qcom,sw-jeita-enable"); - rc = of_property_read_u32_array(node, "qcom,step-soc-thresholds", - chip->dt.step_soc_threshold, - STEP_CHARGING_MAX_STEPS - 1); - if (rc < 0) - chg->step_chg_enabled = false; - - if (of_property_count_u32_elems(node, "qcom,step-current-deltas") - != STEP_CHARGING_MAX_STEPS) - chg->step_chg_enabled = false; - - rc = of_property_read_u32_array(node, "qcom,step-current-deltas", - chip->dt.step_cc_delta, - STEP_CHARGING_MAX_STEPS); - if (rc < 0) - chg->step_chg_enabled = false; + rc = of_property_read_u32(node, "qcom,wd-bark-time-secs", + &chip->dt.wd_bark_time); + if (rc < 0 || chip->dt.wd_bark_time < MIN_WD_BARK_TIME) + chip->dt.wd_bark_time = DEFAULT_WD_BARK_TIME; chip->dt.no_battery = of_property_read_bool(node, "qcom,batteryless-platform"); @@ -418,7 +330,6 @@ static int smb2_parse_dt(struct smb2 *chip) static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_VOLTAGE_MIN, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_PD_CURRENT_MAX, @@ -437,6 +348,9 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_HW_CURRENT_MAX, POWER_SUPPLY_PROP_REAL_TYPE, POWER_SUPPLY_PROP_PR_SWAP, + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, + POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, + POWER_SUPPLY_PROP_SDP_CURRENT_MAX, }; static int smb2_usb_get_prop(struct power_supply *psy, @@ -468,20 +382,17 @@ static int smb2_usb_get_prop(struct power_supply *psy, if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN) val->intval = 0; break; - case POWER_SUPPLY_PROP_VOLTAGE_MIN: - val->intval = chg->voltage_min_uv; - break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: - val->intval = chg->voltage_max_uv; + rc = smblib_get_prop_usb_voltage_max(chg, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: rc = smblib_get_prop_usb_voltage_now(chg, val); break; case POWER_SUPPLY_PROP_PD_CURRENT_MAX: - rc = smblib_get_prop_pd_current_max(chg, val); + val->intval = get_client_vote(chg->usb_icl_votable, PD_VOTER); break; case POWER_SUPPLY_PROP_CURRENT_MAX: - rc = smblib_get_prop_usb_current_max(chg, val); + rc = smblib_get_prop_input_current_settled(chg, val); break; case POWER_SUPPLY_PROP_TYPE: val->intval = POWER_SUPPLY_TYPE_USB_PD; @@ -545,6 +456,16 @@ static int smb2_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PR_SWAP: rc = smblib_get_prop_pr_swap_in_progress(chg, val); break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + val->intval = chg->voltage_max_uv; + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MIN: + val->intval = chg->voltage_min_uv; + break; + case POWER_SUPPLY_PROP_SDP_CURRENT_MAX: + val->intval = get_client_vote(chg->usb_icl_votable, + USB_PSY_VOTER); + break; default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; @@ -572,18 +493,9 @@ static int smb2_usb_set_prop(struct power_supply *psy, } switch (psp) { - case POWER_SUPPLY_PROP_VOLTAGE_MIN: - rc = smblib_set_prop_usb_voltage_min(chg, val); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - rc = smblib_set_prop_usb_voltage_max(chg, val); - break; case POWER_SUPPLY_PROP_PD_CURRENT_MAX: rc = smblib_set_prop_pd_current_max(chg, val); break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - rc = smblib_set_prop_usb_current_max(chg, val); - break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: rc = smblib_set_prop_typec_power_role(chg, val); break; @@ -606,6 +518,15 @@ static int smb2_usb_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PR_SWAP: rc = smblib_set_prop_pr_swap_in_progress(chg, val); break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + rc = smblib_set_prop_pd_voltage_max(chg, val); + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MIN: + rc = smblib_set_prop_pd_voltage_min(chg, val); + break; + case POWER_SUPPLY_PROP_SDP_CURRENT_MAX: + rc = smblib_set_prop_sdp_current_max(chg, val); + break; default: pr_err("set prop %d is not supported\n", psp); rc = -EINVAL; @@ -621,8 +542,6 @@ static int smb2_usb_prop_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { - case POWER_SUPPLY_PROP_CURRENT_MAX: - case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: case POWER_SUPPLY_PROP_CTM_CURRENT_MAX: return 1; default: @@ -664,6 +583,8 @@ static int smb2_init_usb_psy(struct smb2 *chip) static enum power_supply_property smb2_usb_port_props[] = { POWER_SUPPLY_PROP_TYPE, POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_MAX, }; static int smb2_usb_port_get_prop(struct power_supply *psy, @@ -690,6 +611,12 @@ static int smb2_usb_port_get_prop(struct power_supply *psy, else val->intval = 0; break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = 5000000; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = smblib_get_prop_input_current_settled(chg, val); + break; default: pr_err_ratelimited("Get prop %d is not supported in pc_port\n", psp); @@ -871,6 +798,7 @@ static enum power_supply_property smb2_dc_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_REAL_TYPE, }; static int smb2_dc_get_prop(struct power_supply *psy, @@ -891,6 +819,9 @@ static int smb2_dc_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_MAX: rc = smblib_get_prop_dc_current_max(chg, val); break; + case POWER_SUPPLY_PROP_REAL_TYPE: + val->intval = POWER_SUPPLY_TYPE_WIPOWER; + break; default: return -EINVAL; } @@ -939,7 +870,7 @@ static int smb2_dc_prop_is_writeable(struct power_supply *psy, static const struct power_supply_desc dc_psy_desc = { .name = "dc", - .type = POWER_SUPPLY_TYPE_WIPOWER, + .type = POWER_SUPPLY_TYPE_WIRELESS, .properties = smb2_dc_props, .num_properties = ARRAY_SIZE(smb2_dc_props), .get_property = smb2_dc_get_prop, @@ -989,13 +920,14 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, - POWER_SUPPLY_PROP_STEP_CHARGING_STEP, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, POWER_SUPPLY_PROP_CHARGE_DONE, POWER_SUPPLY_PROP_PARALLEL_DISABLE, POWER_SUPPLY_PROP_SET_SHIP_MODE, POWER_SUPPLY_PROP_DIE_HEALTH, POWER_SUPPLY_PROP_RERUN_AICL, POWER_SUPPLY_PROP_DP_DM, + POWER_SUPPLY_PROP_CHARGE_COUNTER, }; static int smb2_batt_get_prop(struct power_supply *psy, @@ -1047,8 +979,8 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: val->intval = chg->step_chg_enabled; break; - case POWER_SUPPLY_PROP_STEP_CHARGING_STEP: - rc = smblib_get_prop_step_chg_step(chg, val); + case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: + val->intval = chg->sw_jeita_enabled; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: rc = smblib_get_prop_batt_voltage_now(chg, val); @@ -1101,6 +1033,9 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_RERUN_AICL: val->intval = 0; break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + rc = smblib_get_prop_batt_charge_counter(chg, val); + break; default: pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; @@ -1163,6 +1098,16 @@ static int smb2_batt_set_prop(struct power_supply *psy, vote(chg->fcc_votable, BATT_PROFILE_VOTER, false, 0); } break; + case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: + chg->step_chg_enabled = !!val->intval; + break; + case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: + if (chg->sw_jeita_enabled != (!!val->intval)) { + rc = smblib_disable_hw_jeita(chg, !!val->intval); + if (rc == 0) + chg->sw_jeita_enabled = !!val->intval; + } + break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: chg->batt_profile_fcc_ua = val->intval; vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, val->intval); @@ -1203,6 +1148,8 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_DP_DM: case POWER_SUPPLY_PROP_RERUN_AICL: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: + case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: return 1; default: break; @@ -1330,73 +1277,6 @@ static int smb2_init_vconn_regulator(struct smb2 *chip) /*************************** * HARDWARE INITIALIZATION * ***************************/ -static int smb2_config_step_charging(struct smb2 *chip) -{ - struct smb_charger *chg = &chip->chg; - int rc = 0; - int i; - - if (!chg->step_chg_enabled) - return rc; - - for (i = 0; i < STEP_CHARGING_MAX_STEPS - 1; i++) { - rc = smblib_set_charge_param(chg, - &chg->param.step_soc_threshold[i], - chip->dt.step_soc_threshold[i]); - if (rc < 0) { - pr_err("Couldn't configure soc thresholds rc = %d\n", - rc); - goto err_out; - } - } - - for (i = 0; i < STEP_CHARGING_MAX_STEPS; i++) { - rc = smblib_set_charge_param(chg, &chg->param.step_cc_delta[i], - chip->dt.step_cc_delta[i]); - if (rc < 0) { - pr_err("Couldn't configure cc delta rc = %d\n", - rc); - goto err_out; - } - } - - rc = smblib_write(chg, STEP_CHG_UPDATE_REQUEST_TIMEOUT_CFG_REG, - STEP_CHG_UPDATE_REQUEST_TIMEOUT_40S); - if (rc < 0) { - dev_err(chg->dev, - "Couldn't configure soc request timeout reg rc=%d\n", - rc); - goto err_out; - } - - rc = smblib_write(chg, STEP_CHG_UPDATE_FAIL_TIMEOUT_CFG_REG, - STEP_CHG_UPDATE_FAIL_TIMEOUT_120S); - if (rc < 0) { - dev_err(chg->dev, - "Couldn't configure soc fail timeout reg rc=%d\n", - rc); - goto err_out; - } - - /* - * enable step charging, source soc, standard mode, go to final - * state in case of failure. - */ - rc = smblib_write(chg, CHGR_STEP_CHG_MODE_CFG_REG, - STEP_CHARGING_ENABLE_BIT | - STEP_CHARGING_SOURCE_SELECT_BIT | - STEP_CHARGING_SOC_FAIL_OPTION_BIT); - if (rc < 0) { - dev_err(chg->dev, "Couldn't configure charger rc=%d\n", rc); - goto err_out; - } - - return 0; -err_out: - chg->step_chg_enabled = false; - return rc; -} - static int smb2_config_wipower_input_power(struct smb2 *chip, int uw) { int rc; @@ -1572,7 +1452,7 @@ static int smb2_init_hw(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; int rc; - u8 stat; + u8 stat, val; if (chip->dt.no_battery) chg->fake_capacity = 50; @@ -1720,11 +1600,27 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } - /* configure step charging */ - rc = smb2_config_step_charging(chip); - if (rc < 0) { - dev_err(chg->dev, "Couldn't configure step charging rc=%d\n", - rc); + val = (ilog2(chip->dt.wd_bark_time / 16) << BARK_WDOG_TIMEOUT_SHIFT) & + BARK_WDOG_TIMEOUT_MASK; + val |= BITE_WDOG_TIMEOUT_8S; + rc = smblib_masked_write(chg, SNARL_BARK_BITE_WD_CFG_REG, + BITE_WDOG_DISABLE_CHARGING_CFG_BIT | + BARK_WDOG_TIMEOUT_MASK | BITE_WDOG_TIMEOUT_MASK, + val); + if (rc) { + pr_err("Couldn't configue WD config rc=%d\n", rc); + return rc; + } + + /* enable WD BARK and enable it on plugin */ + rc = smblib_masked_write(chg, WD_CFG_REG, + WATCHDOG_TRIGGER_AFP_EN_BIT | + WDOG_TIMER_EN_ON_PLUGIN_BIT | + BARK_WDOG_INT_EN_BIT, + WDOG_TIMER_EN_ON_PLUGIN_BIT | + BARK_WDOG_INT_EN_BIT); + if (rc) { + pr_err("Couldn't configue WD config rc=%d\n", rc); return rc; } @@ -1783,6 +1679,13 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } + rc = smblib_read(chg, USBIN_OPTIONS_2_CFG_REG, &chg->float_cfg); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read float charger options rc=%d\n", + rc); + return rc; + } + switch (chip->dt.chg_inhibit_thr_mv) { case 50: rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG, @@ -1839,6 +1742,14 @@ static int smb2_init_hw(struct smb2 *chip) } } + if (chg->sw_jeita_enabled) { + rc = smblib_disable_hw_jeita(chg, true); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set hw jeita rc=%d\n", rc); + return rc; + } + } + return rc; } @@ -1911,8 +1822,8 @@ static int smb2_chg_config_init(struct smb2 *chip) chip->chg.wa_flags |= BOOST_BACK_WA | OTG_WA; chg->param.freq_buck = pm660_params.freq_buck; chg->param.freq_boost = pm660_params.freq_boost; - chg->chg_freq.freq_5V = 600; - chg->chg_freq.freq_6V_8V = 800; + chg->chg_freq.freq_5V = 650; + chg->chg_freq.freq_6V_8V = 850; chg->chg_freq.freq_9V = 1050; chg->chg_freq.freq_12V = 1200; chg->chg_freq.freq_removal = 1050; @@ -1944,8 +1855,8 @@ static int smb2_determine_initial_status(struct smb2 *chip) smblib_handle_usb_source_change(0, &irq_data); smblib_handle_chg_state_change(0, &irq_data); smblib_handle_icl_change(0, &irq_data); - smblib_handle_step_chg_state_change(0, &irq_data); - smblib_handle_step_chg_soc_update_request(0, &irq_data); + smblib_handle_batt_temp_changed(0, &irq_data); + smblib_handle_wdog_bark(0, &irq_data); return 0; } @@ -1967,18 +1878,15 @@ static struct smb_irq_info smb2_irqs[] = { }, [STEP_CHG_STATE_CHANGE_IRQ] = { .name = "step-chg-state-change", - .handler = smblib_handle_step_chg_state_change, - .wake = true, + .handler = NULL, }, [STEP_CHG_SOC_UPDATE_FAIL_IRQ] = { .name = "step-chg-soc-update-fail", - .handler = smblib_handle_step_chg_soc_update_fail, - .wake = true, + .handler = NULL, }, [STEP_CHG_SOC_UPDATE_REQ_IRQ] = { .name = "step-chg-soc-update-request", - .handler = smblib_handle_step_chg_soc_update_request, - .wake = true, + .handler = NULL, }, /* OTG IRQs */ [OTG_FAIL_IRQ] = { @@ -2001,6 +1909,7 @@ static struct smb_irq_info smb2_irqs[] = { [BATT_TEMP_IRQ] = { .name = "bat-temp", .handler = smblib_handle_batt_temp_changed, + .wake = true, }, [BATT_OCP_IRQ] = { .name = "bat-ocp", @@ -2096,7 +2005,8 @@ static struct smb_irq_info smb2_irqs[] = { }, [WDOG_BARK_IRQ] = { .name = "wdog-bark", - .handler = NULL, + .handler = smblib_handle_wdog_bark, + .wake = true, }, [AICL_FAIL_IRQ] = { .name = "aicl-fail", @@ -2336,15 +2246,15 @@ static int smb2_probe(struct platform_device *pdev) return rc; } - rc = smblib_init(chg); + rc = smb2_parse_dt(chip); if (rc < 0) { - pr_err("Smblib_init failed rc=%d\n", rc); + pr_err("Couldn't parse device tree rc=%d\n", rc); goto cleanup; } - rc = smb2_parse_dt(chip); + rc = smblib_init(chg); if (rc < 0) { - pr_err("Couldn't parse device tree rc=%d\n", rc); + pr_err("Smblib_init failed rc=%d\n", rc); goto cleanup; } diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 64d71727f7e6..f9d35ea7775b 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -23,6 +23,7 @@ #include "smb-lib.h" #include "smb-reg.h" #include "battery.h" +#include "step-chg-jeita.h" #include "storm-watch.h" #define smblib_err(chg, fmt, ...) \ @@ -102,35 +103,6 @@ unlock: return rc; } -static int smblib_get_step_cc_delta(struct smb_charger *chg, int *cc_delta_ua) -{ - int rc, step_state; - u8 stat; - - if (!chg->step_chg_enabled) { - *cc_delta_ua = 0; - return 0; - } - - rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", - rc); - return rc; - } - - step_state = (stat & STEP_CHARGING_STATUS_MASK) >> - STEP_CHARGING_STATUS_SHIFT; - rc = smblib_get_charge_param(chg, &chg->param.step_cc_delta[step_state], - cc_delta_ua); - if (rc < 0) { - smblib_err(chg, "Couldn't get step cc delta rc=%d\n", rc); - return rc; - } - - return 0; -} - static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua) { int rc, cc_minus_ua; @@ -149,7 +121,7 @@ static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua) } rc = smblib_get_charge_param(chg, &chg->param.jeita_cc_comp, - &cc_minus_ua); + &cc_minus_ua); if (rc < 0) { smblib_err(chg, "Couldn't get jeita cc minus rc=%d\n", rc); return rc; @@ -402,28 +374,6 @@ int smblib_set_charge_param(struct smb_charger *chg, return rc; } -static int step_charge_soc_update(struct smb_charger *chg, int capacity) -{ - int rc = 0; - - rc = smblib_set_charge_param(chg, &chg->param.step_soc, capacity); - if (rc < 0) { - smblib_err(chg, "Error in updating soc, rc=%d\n", rc); - return rc; - } - - rc = smblib_write(chg, STEP_CHG_SOC_VBATT_V_UPDATE_REG, - STEP_CHG_SOC_VBATT_V_UPDATE_BIT); - if (rc < 0) { - smblib_err(chg, - "Couldn't set STEP_CHG_SOC_VBATT_V_UPDATE_REG rc=%d\n", - rc); - return rc; - } - - return rc; -} - int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) { int rc = 0; @@ -470,19 +420,21 @@ static int smblib_set_adapter_allowance(struct smb_charger *chg, { int rc = 0; - switch (allowed_voltage) { - case USBIN_ADAPTER_ALLOW_12V: - case USBIN_ADAPTER_ALLOW_5V_OR_12V: - case USBIN_ADAPTER_ALLOW_9V_TO_12V: - case USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V: - case USBIN_ADAPTER_ALLOW_5V_TO_12V: - /* PM660 only support max. 9V */ - if (chg->smb_version == PM660_SUBTYPE) { - smblib_dbg(chg, PR_MISC, "voltage not supported=%d\n", - allowed_voltage); + /* PM660 only support max. 9V */ + if (chg->smb_version == PM660_SUBTYPE) { + switch (allowed_voltage) { + case USBIN_ADAPTER_ALLOW_12V: + case USBIN_ADAPTER_ALLOW_9V_TO_12V: + allowed_voltage = USBIN_ADAPTER_ALLOW_9V; + break; + case USBIN_ADAPTER_ALLOW_5V_OR_12V: + case USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V: allowed_voltage = USBIN_ADAPTER_ALLOW_5V_OR_9V; + break; + case USBIN_ADAPTER_ALLOW_5V_TO_12V: + allowed_voltage = USBIN_ADAPTER_ALLOW_5V_TO_9V; + break; } - break; } rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage); @@ -538,6 +490,45 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, /******************** * HELPER FUNCTIONS * ********************/ +static int smblib_request_dpdm(struct smb_charger *chg, bool enable) +{ + int rc = 0; + + /* fetch the DPDM regulator */ + if (!chg->dpdm_reg && of_get_property(chg->dev->of_node, + "dpdm-supply", NULL)) { + chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm"); + if (IS_ERR(chg->dpdm_reg)) { + rc = PTR_ERR(chg->dpdm_reg); + smblib_err(chg, "Couldn't get dpdm regulator rc=%d\n", + rc); + chg->dpdm_reg = NULL; + return rc; + } + } + + if (enable) { + if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); + rc = regulator_enable(chg->dpdm_reg); + if (rc < 0) + smblib_err(chg, + "Couldn't enable dpdm regulator rc=%d\n", + rc); + } + } else { + if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); + rc = regulator_disable(chg->dpdm_reg); + if (rc < 0) + smblib_err(chg, + "Couldn't disable dpdm regulator rc=%d\n", + rc); + } + } + + return rc; +} static void smblib_rerun_apsd(struct smb_charger *chg) { @@ -564,10 +555,17 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); /* if PD is active, APSD is disabled so won't have a valid result */ - if (chg->pd_active) + if (chg->pd_active) { chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD; - else + } else { + /* + * Update real charger type only if its not FLOAT + * detected as as SDP + */ + if (!(apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT && + chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) chg->real_charger_type = apsd_result->pst; + } smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n", apsd_result->name, chg->pd_active); @@ -650,13 +648,9 @@ static void smblib_uusb_removal(struct smb_charger *chg) cancel_delayed_work_sync(&chg->pl_enable_work); - if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) { - smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); - rc = regulator_disable(chg->dpdm_reg); - if (rc < 0) - smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n", - rc); - } + rc = smblib_request_dpdm(chg, false); + if (rc < 0) + smblib_err(chg, "Couldn't to disable DPDM rc=%d\n", rc); if (chg->wa_flags & BOOST_BACK_WA) { data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data; @@ -748,24 +742,9 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) if (!val.intval) return 0; - /* fetch the DPDM regulator */ - if (!chg->dpdm_reg && of_get_property(chg->dev->of_node, - "dpdm-supply", NULL)) { - chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm"); - if (IS_ERR(chg->dpdm_reg)) { - smblib_err(chg, "Couldn't get dpdm regulator rc=%ld\n", - PTR_ERR(chg->dpdm_reg)); - chg->dpdm_reg = NULL; - } - } - - if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) { - smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); - rc = regulator_enable(chg->dpdm_reg); - if (rc < 0) - smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n", - rc); - } + rc = smblib_request_dpdm(chg, true); + if (rc < 0) + smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); chg->uusb_apsd_rerun_done = true; smblib_rerun_apsd(chg); @@ -835,6 +814,7 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) { int rc; u8 icl_options; + const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); /* power source is SDP */ switch (icl_ua) { @@ -859,6 +839,21 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) return -EINVAL; } + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB && + apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT) { + /* + * change the float charger configuration to SDP, if this + * is the case of SDP being detected as FLOAT + */ + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + FORCE_FLOAT_SDP_CFG_BIT, FORCE_FLOAT_SDP_CFG_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't set float ICL options rc=%d\n", + rc); + return rc; + } + } + rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, CFG_USB3P0_SEL_BIT | USB51_MODE_BIT, icl_options); if (rc < 0) { @@ -1578,8 +1573,8 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, union power_supply_propval *val) { union power_supply_propval pval = {0, }; - bool usb_online, dc_online; - u8 stat; + bool usb_online, dc_online, qnovo_en; + u8 stat, pt_en_cmd; int rc; rc = smblib_get_prop_usb_online(chg, &pval); @@ -1647,11 +1642,22 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", rc); return rc; - } + } stat &= ENABLE_TRICKLE_BIT | ENABLE_PRE_CHARGING_BIT | ENABLE_FAST_CHARGING_BIT | ENABLE_FULLON_MODE_BIT; - if (!stat) + + rc = smblib_read(chg, QNOVO_PT_ENABLE_CMD_REG, &pt_en_cmd); + if (rc < 0) { + smblib_err(chg, "Couldn't read QNOVO_PT_ENABLE_CMD_REG rc=%d\n", + rc); + return rc; + } + + qnovo_en = (bool)(pt_en_cmd & QNOVO_PT_ENABLE_CMD_BIT); + + /* ignore stat7 when qnovo is enabled */ + if (!qnovo_en && !stat) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; return 0; @@ -1804,30 +1810,6 @@ int smblib_get_prop_batt_temp(struct smb_charger *chg, return rc; } -int smblib_get_prop_step_chg_step(struct smb_charger *chg, - union power_supply_propval *val) -{ - int rc; - u8 stat; - - if (!chg->step_chg_enabled) { - val->intval = -1; - return 0; - } - - rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", - rc); - return rc; - } - - val->intval = (stat & STEP_CHARGING_STATUS_MASK) >> - STEP_CHARGING_STATUS_SHIFT; - - return rc; -} - int smblib_get_prop_batt_charge_done(struct smb_charger *chg, union power_supply_propval *val) { @@ -1863,6 +1845,19 @@ int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg, return 0; } +int smblib_get_prop_batt_charge_counter(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->bms_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_CHARGE_COUNTER, val); + return rc; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ @@ -2073,6 +2068,29 @@ int smblib_dp_dm(struct smb_charger *chg, int val) return rc; } +int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable) +{ + int rc; + u8 mask; + + /* + * Disable h/w base JEITA compensation if s/w JEITA is enabled + */ + mask = JEITA_EN_COLD_SL_FCV_BIT + | JEITA_EN_HOT_SL_FCV_BIT + | JEITA_EN_HOT_SL_CCC_BIT + | JEITA_EN_COLD_SL_CCC_BIT, + rc = smblib_masked_write(chg, JEITA_EN_CFG_REG, mask, + disable ? 0 : mask); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure s/w jeita rc=%d\n", + rc); + return rc; + } + return 0; +} + /******************* * DC PSY GETTERS * *******************/ @@ -2184,6 +2202,25 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, return rc; } +int smblib_get_prop_usb_voltage_max(struct smb_charger *chg, + union power_supply_propval *val) +{ + switch (chg->real_charger_type) { + case POWER_SUPPLY_TYPE_USB_HVDCP: + case POWER_SUPPLY_TYPE_USB_PD: + if (chg->smb_version == PM660_SUBTYPE) + val->intval = MICRO_9V; + else + val->intval = MICRO_12V; + break; + default: + val->intval = MICRO_5V; + break; + } + + return 0; +} + int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, union power_supply_propval *val) { @@ -2203,21 +2240,6 @@ int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, return iio_read_channel_processed(chg->iio.usbin_v_chan, &val->intval); } -int smblib_get_prop_pd_current_max(struct smb_charger *chg, - union power_supply_propval *val) -{ - val->intval = get_client_vote_locked(chg->usb_icl_votable, PD_VOTER); - return 0; -} - -int smblib_get_prop_usb_current_max(struct smb_charger *chg, - union power_supply_propval *val) -{ - val->intval = get_client_vote_locked(chg->usb_icl_votable, - USB_PSY_VOTER); - return 0; -} - int smblib_get_prop_usb_current_now(struct smb_charger *chg, union power_supply_propval *val) { @@ -2395,16 +2417,9 @@ int smblib_get_prop_input_current_settled(struct smb_charger *chg, int smblib_get_prop_input_voltage_settled(struct smb_charger *chg, union power_supply_propval *val) { - const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); int rc, pulses; - val->intval = MICRO_5V; - if (apsd_result == NULL) { - smblib_err(chg, "APSD result is NULL\n"); - return 0; - } - - switch (apsd_result->pst) { + switch (chg->real_charger_type) { case POWER_SUPPLY_TYPE_USB_HVDCP_3: rc = smblib_get_pulse_cnt(chg, &pulses); if (rc < 0) { @@ -2414,6 +2429,9 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg, } val->intval = MICRO_5V + HVDCP3_STEP_UV * pulses; break; + case POWER_SUPPLY_TYPE_USB_PD: + val->intval = chg->voltage_min_uv; + break; default: val->intval = MICRO_5V; break; @@ -2476,6 +2494,31 @@ int smblib_get_prop_die_health(struct smb_charger *chg, return 0; } +#define SDP_CURRENT_UA 500000 +#define CDP_CURRENT_UA 1500000 +#define DCP_CURRENT_UA 1500000 +#define HVDCP_CURRENT_UA 3000000 +#define TYPEC_DEFAULT_CURRENT_UA 900000 +#define TYPEC_MEDIUM_CURRENT_UA 1500000 +#define TYPEC_HIGH_CURRENT_UA 3000000 +static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) +{ + int rp_ua; + + switch (typec_mode) { + case POWER_SUPPLY_TYPEC_SOURCE_HIGH: + rp_ua = TYPEC_HIGH_CURRENT_UA; + break; + case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: + case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: + /* fall through */ + default: + rp_ua = DCP_CURRENT_UA; + } + + return rp_ua; +} + /******************* * USB PSY SETTERS * * *****************/ @@ -2493,14 +2536,54 @@ int smblib_set_prop_pd_current_max(struct smb_charger *chg, return rc; } -int smblib_set_prop_usb_current_max(struct smb_charger *chg, +static int smblib_handle_usb_current(struct smb_charger *chg, + int usb_current) +{ + int rc = 0, rp_ua, typec_mode; + + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) { + if (usb_current == -ETIMEDOUT) { + /* + * Valid FLOAT charger, report the current based + * of Rp + */ + typec_mode = smblib_get_prop_typec_mode(chg); + rp_ua = get_rp_based_dcp_current(chg, typec_mode); + rc = vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, + true, rp_ua); + if (rc < 0) + return rc; + } else { + /* + * FLOAT charger detected as SDP by USB driver, + * charge with the requested current and update the + * real_charger_type + */ + chg->real_charger_type = POWER_SUPPLY_TYPE_USB; + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + true, usb_current); + if (rc < 0) + return rc; + rc = vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, + false, 0); + if (rc < 0) + return rc; + } + } else { + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + true, usb_current); + } + + return rc; +} + +int smblib_set_prop_sdp_current_max(struct smb_charger *chg, const union power_supply_propval *val) { int rc = 0; if (!chg->pd_active) { - rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, - true, val->intval); + rc = smblib_handle_usb_current(chg, val->intval); } else if (chg->system_suspend_supported) { if (val->intval <= USBIN_25MA) rc = vote(chg->usb_icl_votable, @@ -2581,7 +2664,7 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, return rc; } -int smblib_set_prop_usb_voltage_min(struct smb_charger *chg, +int smblib_set_prop_pd_voltage_min(struct smb_charger *chg, const union power_supply_propval *val) { int rc, min_uv; @@ -2596,10 +2679,11 @@ int smblib_set_prop_usb_voltage_min(struct smb_charger *chg, } chg->voltage_min_uv = min_uv; + power_supply_changed(chg->usb_main_psy); return rc; } -int smblib_set_prop_usb_voltage_max(struct smb_charger *chg, +int smblib_set_prop_pd_voltage_max(struct smb_charger *chg, const union power_supply_propval *val) { int rc, max_uv; @@ -2883,46 +2967,72 @@ int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg, return rc; } +static int smblib_recover_from_soft_jeita(struct smb_charger *chg) +{ + u8 stat_1, stat_2; + int rc; + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat_1); + if (rc < 0) { + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + rc); + return rc; + } + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat_2); + if (rc < 0) { + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", + rc); + return rc; + } + + if ((chg->jeita_status && !(stat_2 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK) && + ((stat_1 & BATTERY_CHARGER_STATUS_MASK) == TERMINATE_CHARGE))) { + /* + * We are moving from JEITA soft -> Normal and charging + * is terminated + */ + rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG, 0); + if (rc < 0) { + smblib_err(chg, "Couldn't disable charging rc=%d\n", + rc); + return rc; + } + rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't enable charging rc=%d\n", + rc); + return rc; + } + } + + chg->jeita_status = stat_2 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK; + + return 0; +} + /*********************** * USB MAIN PSY GETTERS * *************************/ int smblib_get_prop_fcc_delta(struct smb_charger *chg, - union power_supply_propval *val) + union power_supply_propval *val) { - int rc, jeita_cc_delta_ua, step_cc_delta_ua, hw_cc_delta_ua = 0; - - rc = smblib_get_step_cc_delta(chg, &step_cc_delta_ua); - if (rc < 0) { - smblib_err(chg, "Couldn't get step cc delta rc=%d\n", rc); - step_cc_delta_ua = 0; - } else { - hw_cc_delta_ua = step_cc_delta_ua; - } + int rc, jeita_cc_delta_ua = 0; rc = smblib_get_jeita_cc_delta(chg, &jeita_cc_delta_ua); if (rc < 0) { smblib_err(chg, "Couldn't get jeita cc delta rc=%d\n", rc); jeita_cc_delta_ua = 0; - } else if (jeita_cc_delta_ua < 0) { - /* HW will take the min between JEITA and step charge */ - hw_cc_delta_ua = min(hw_cc_delta_ua, jeita_cc_delta_ua); } - val->intval = hw_cc_delta_ua; + val->intval = jeita_cc_delta_ua; return 0; } /*********************** * USB MAIN PSY SETTERS * *************************/ - -#define SDP_CURRENT_UA 500000 -#define CDP_CURRENT_UA 1500000 -#define DCP_CURRENT_UA 1500000 -#define HVDCP_CURRENT_UA 3000000 -#define TYPEC_DEFAULT_CURRENT_UA 900000 -#define TYPEC_MEDIUM_CURRENT_UA 1500000 -#define TYPEC_HIGH_CURRENT_UA 3000000 int smblib_get_charge_current(struct smb_charger *chg, int *total_current_ua) { @@ -3089,62 +3199,19 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data) return IRQ_HANDLED; } -irqreturn_t smblib_handle_step_chg_state_change(int irq, void *data) -{ - struct smb_irq_data *irq_data = data; - struct smb_charger *chg = irq_data->parent_data; - - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); - - if (chg->step_chg_enabled) - rerun_election(chg->fcc_votable); - - return IRQ_HANDLED; -} - -irqreturn_t smblib_handle_step_chg_soc_update_fail(int irq, void *data) -{ - struct smb_irq_data *irq_data = data; - struct smb_charger *chg = irq_data->parent_data; - - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); - - if (chg->step_chg_enabled) - rerun_election(chg->fcc_votable); - - return IRQ_HANDLED; -} - -#define STEP_SOC_REQ_MS 3000 -irqreturn_t smblib_handle_step_chg_soc_update_request(int irq, void *data) +irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; int rc; - union power_supply_propval pval = {0, }; - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); - - if (!chg->bms_psy) { - schedule_delayed_work(&chg->step_soc_req_work, - msecs_to_jiffies(STEP_SOC_REQ_MS)); + rc = smblib_recover_from_soft_jeita(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't recover chg from soft jeita rc=%d\n", + rc); return IRQ_HANDLED; } - rc = smblib_get_prop_batt_capacity(chg, &pval); - if (rc < 0) - smblib_err(chg, "Couldn't get batt capacity rc=%d\n", rc); - else - step_charge_soc_update(chg, pval.intval); - - return IRQ_HANDLED; -} - -irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data) -{ - struct smb_irq_data *irq_data = data; - struct smb_charger *chg = irq_data->parent_data; - rerun_election(chg->fcc_votable); power_supply_changed(chg->batt_psy); return IRQ_HANDLED; @@ -3256,25 +3323,10 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) smblib_set_opt_freq_buck(chg, vbus_rising ? chg->chg_freq.freq_5V : chg->chg_freq.freq_removal); - /* fetch the DPDM regulator */ - if (!chg->dpdm_reg && of_get_property(chg->dev->of_node, - "dpdm-supply", NULL)) { - chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm"); - if (IS_ERR(chg->dpdm_reg)) { - smblib_err(chg, "Couldn't get dpdm regulator rc=%ld\n", - PTR_ERR(chg->dpdm_reg)); - chg->dpdm_reg = NULL; - } - } - if (vbus_rising) { - if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) { - smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); - rc = regulator_enable(chg->dpdm_reg); - if (rc < 0) - smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n", - rc); - } + rc = smblib_request_dpdm(chg, true); + if (rc < 0) + smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); /* Schedule work to enable parallel charger */ vote(chg->awake_votable, PL_DELAY_VOTER, true, 0); @@ -3294,13 +3346,9 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) } } - if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) { - smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); - rc = regulator_disable(chg->dpdm_reg); - if (rc < 0) - smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n", - rc); - } + rc = smblib_request_dpdm(chg, false); + if (rc < 0) + smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); } if (chg->micro_usb_mode) @@ -3521,24 +3569,6 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, rising ? "rising" : "falling"); } -static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) -{ - int rp_ua; - - switch (typec_mode) { - case POWER_SUPPLY_TYPEC_SOURCE_HIGH: - rp_ua = TYPEC_HIGH_CURRENT_UA; - break; - case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: - case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: - /* fall through */ - default: - rp_ua = DCP_CURRENT_UA; - } - - return rp_ua; -} - static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) { int typec_mode; @@ -3564,11 +3594,17 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000); break; case POWER_SUPPLY_TYPE_USB_DCP: - case POWER_SUPPLY_TYPE_USB_FLOAT: typec_mode = smblib_get_prop_typec_mode(chg); rp_ua = get_rp_based_dcp_current(chg, typec_mode); vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua); break; + case POWER_SUPPLY_TYPE_USB_FLOAT: + /* + * limit ICL to 100mA, the USB driver will enumerate to check + * if this is a SDP and appropriately set the current + */ + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); + break; case POWER_SUPPLY_TYPE_USB_HVDCP: case POWER_SUPPLY_TYPE_USB_HVDCP_3: vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 3000000); @@ -3580,6 +3616,32 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) } } +static void smblib_notify_extcon_props(struct smb_charger *chg) +{ + union power_supply_propval val; + + smblib_get_prop_typec_cc_orientation(chg, &val); + extcon_set_cable_state_(chg->extcon, EXTCON_USB_CC, + (val.intval == 2) ? 1 : 0); + extcon_set_cable_state_(chg->extcon, EXTCON_USB_SPEED, true); +} + +static void smblib_notify_device_mode(struct smb_charger *chg, bool enable) +{ + if (enable) + smblib_notify_extcon_props(chg); + + extcon_set_cable_state_(chg->extcon, EXTCON_USB, enable); +} + +static void smblib_notify_usb_host(struct smb_charger *chg, bool enable) +{ + if (enable) + smblib_notify_extcon_props(chg); + + extcon_set_cable_state_(chg->extcon, EXTCON_USB_HOST, enable); +} + #define HVDCP_DET_MS 2500 static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { @@ -3599,6 +3661,8 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) if (chg->micro_usb_mode) extcon_set_cable_state_(chg->extcon, EXTCON_USB, true); + if (chg->use_extcon) + smblib_notify_device_mode(chg, true); case OCP_CHARGER_BIT: case FLOAT_CHARGER_BIT: /* if not DCP then no hvdcp timeout happens, Enable pd here. */ @@ -3683,6 +3747,10 @@ static void typec_sink_insertion(struct smb_charger *chg) */ vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, false, 0); + if (chg->use_extcon) { + smblib_notify_usb_host(chg, true); + chg->otg_present = true; + } } static void typec_sink_removal(struct smb_charger *chg) @@ -3700,13 +3768,9 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->cc2_detach_wa_active = false; - if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) { - smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); - rc = regulator_disable(chg->dpdm_reg); - if (rc < 0) - smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n", - rc); - } + rc = smblib_request_dpdm(chg, false); + if (rc < 0) + smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); if (chg->wa_flags & BOOST_BACK_WA) { data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data; @@ -3763,6 +3827,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->pd_hard_reset = 0; chg->typec_legacy_valid = false; + /* write back the default FLOAT charger configuration */ + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + (u8)FLOAT_OPTIONS_MASK, chg->float_cfg); + if (rc < 0) + smblib_err(chg, "Couldn't write float charger options rc=%d\n", + rc); + /* reset back to 120mS tCC debounce */ rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, 0); if (rc < 0) @@ -3828,6 +3899,14 @@ unlock: typec_sink_removal(chg); smblib_update_usb_type(chg); + + if (chg->use_extcon) { + if (chg->otg_present) + smblib_notify_usb_host(chg, false); + else + smblib_notify_device_mode(chg, false); + } + chg->otg_present = false; } static void smblib_handle_typec_insertion(struct smb_charger *chg) @@ -3842,10 +3921,14 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg) smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n", rc); - if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT) + if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT) { typec_sink_insertion(chg); - else + } else { + rc = smblib_request_dpdm(chg, true); + if (rc < 0) + smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); typec_sink_removal(chg); + } } static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) @@ -3858,6 +3941,24 @@ static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) return; /* + * if APSD indicates FLOAT and the USB stack had detected SDP, + * do not respond to Rp changes as we do not confirm that its + * a legacy cable + */ + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB) + return; + /* + * We want the ICL vote @ 100mA for a FLOAT charger + * until the detection by the USB stack is complete. + * Ignore the Rp changes unless there is a + * pre-existing valid vote. + */ + if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT && + get_client_vote(chg->usb_icl_votable, + LEGACY_UNKNOWN_VOTER) <= 100000) + return; + + /* * handle Rp change for DCP/FLOAT/OCP. * Update the current only if the Rp is different from * the last Rp value. @@ -4042,10 +4143,15 @@ irqreturn_t smblib_handle_wdog_bark(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; int rc; + smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); + rc = smblib_write(chg, BARK_BITE_WDOG_PET_REG, BARK_BITE_WDOG_PET_BIT); if (rc < 0) smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc); + if (chg->step_chg_enabled || chg->sw_jeita_enabled) + power_supply_changed(chg->batt_psy); + return IRQ_HANDLED; } @@ -4128,22 +4234,6 @@ static void bms_update_work(struct work_struct *work) power_supply_changed(chg->batt_psy); } -static void step_soc_req_work(struct work_struct *work) -{ - struct smb_charger *chg = container_of(work, struct smb_charger, - step_soc_req_work.work); - union power_supply_propval pval = {0, }; - int rc; - - rc = smblib_get_prop_batt_capacity(chg, &pval); - if (rc < 0) { - smblib_err(chg, "Couldn't get batt capacity rc=%d\n", rc); - return; - } - - step_charge_soc_update(chg, pval.intval); -} - static void clear_hdc_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, @@ -4676,7 +4766,6 @@ int smblib_init(struct smb_charger *chg) INIT_WORK(&chg->bms_update_work, bms_update_work); INIT_WORK(&chg->rdstd_cc2_detach_work, rdstd_cc2_detach_work); INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work); - INIT_DELAYED_WORK(&chg->step_soc_req_work, step_soc_req_work); INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work); INIT_WORK(&chg->otg_oc_work, smblib_otg_oc_work); INIT_WORK(&chg->vconn_oc_work, smblib_vconn_oc_work); @@ -4698,6 +4787,14 @@ int smblib_init(struct smb_charger *chg) return rc; } + rc = qcom_step_chg_init(chg->step_chg_enabled, + chg->sw_jeita_enabled); + if (rc < 0) { + smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n", + rc); + return rc; + } + rc = smblib_create_votables(chg); if (rc < 0) { smblib_err(chg, "Couldn't create votables rc=%d\n", @@ -4732,7 +4829,6 @@ int smblib_deinit(struct smb_charger *chg) cancel_work_sync(&chg->bms_update_work); cancel_work_sync(&chg->rdstd_cc2_detach_work); cancel_delayed_work_sync(&chg->hvdcp_detect_work); - cancel_delayed_work_sync(&chg->step_soc_req_work); cancel_delayed_work_sync(&chg->clear_hdc_work); cancel_work_sync(&chg->otg_oc_work); cancel_work_sync(&chg->vconn_oc_work); @@ -4744,6 +4840,7 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->bb_removal_work); power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); + qcom_step_chg_deinit(); qcom_batt_deinit(); break; case PARALLEL_SLAVE: diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 18714827b8d2..a89d09711ec8 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -139,9 +139,14 @@ struct smb_irq_info { static const unsigned int smblib_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, + EXTCON_USB_CC, + EXTCON_USB_SPEED, EXTCON_NONE, }; +/* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */ +static const u32 smblib_extcon_exclusive[] = {0x3, 0}; + struct smb_regulator { struct regulator_dev *rdev; struct regulator_desc rdesc; @@ -190,9 +195,6 @@ struct smb_params { struct smb_chg_param dc_icl_div2_mid_hv; struct smb_chg_param dc_icl_div2_hv; struct smb_chg_param jeita_cc_comp; - struct smb_chg_param step_soc_threshold[4]; - struct smb_chg_param step_soc; - struct smb_chg_param step_cc_delta[5]; struct smb_chg_param freq_buck; struct smb_chg_param freq_boost; }; @@ -287,7 +289,6 @@ struct smb_charger { struct work_struct rdstd_cc2_detach_work; struct delayed_work hvdcp_detect_work; struct delayed_work ps_change_timeout_work; - struct delayed_work step_soc_req_work; struct delayed_work clear_hdc_work; struct work_struct otg_oc_work; struct work_struct vconn_oc_work; @@ -310,6 +311,7 @@ struct smb_charger { int dcp_icl_ua; int fake_capacity; bool step_chg_enabled; + bool sw_jeita_enabled; bool is_hdc; bool chg_done; bool micro_usb_mode; @@ -329,6 +331,10 @@ struct smb_charger { bool pr_swap_in_progress; int typec_mode; int usb_icl_change_irq_enabled; + u32 jeita_status; + u8 float_cfg; + bool use_extcon; + bool otg_present; /* workaround flag */ u32 wa_flags; @@ -383,9 +389,6 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev); irqreturn_t smblib_handle_debug(int irq, void *data); irqreturn_t smblib_handle_otg_overcurrent(int irq, void *data); irqreturn_t smblib_handle_chg_state_change(int irq, void *data); -irqreturn_t smblib_handle_step_chg_state_change(int irq, void *data); -irqreturn_t smblib_handle_step_chg_soc_update_fail(int irq, void *data); -irqreturn_t smblib_handle_step_chg_soc_update_request(int irq, void *data); irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data); irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data); irqreturn_t smblib_handle_usb_psy_changed(int irq, void *data); @@ -423,9 +426,8 @@ int smblib_get_prop_batt_current_now(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_temp(struct smb_charger *chg, union power_supply_propval *val); -int smblib_get_prop_step_chg_step(struct smb_charger *chg, +int smblib_get_prop_batt_charge_counter(struct smb_charger *chg, union power_supply_propval *val); - int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_batt_capacity(struct smb_charger *chg, @@ -450,11 +452,9 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_usb_suspend(struct smb_charger *chg, union power_supply_propval *val); -int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, - union power_supply_propval *val); -int smblib_get_prop_pd_current_max(struct smb_charger *chg, +int smblib_get_prop_usb_voltage_max(struct smb_charger *chg, union power_supply_propval *val); -int smblib_get_prop_usb_current_max(struct smb_charger *chg, +int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_usb_current_now(struct smb_charger *chg, union power_supply_propval *val); @@ -482,11 +482,11 @@ int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg, union power_supply_propval *val); int smblib_set_prop_pd_current_max(struct smb_charger *chg, const union power_supply_propval *val); -int smblib_set_prop_usb_current_max(struct smb_charger *chg, +int smblib_set_prop_sdp_current_max(struct smb_charger *chg, const union power_supply_propval *val); -int smblib_set_prop_usb_voltage_min(struct smb_charger *chg, +int smblib_set_prop_pd_voltage_max(struct smb_charger *chg, const union power_supply_propval *val); -int smblib_set_prop_usb_voltage_max(struct smb_charger *chg, +int smblib_set_prop_pd_voltage_min(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_boost_current(struct smb_charger *chg, const union power_supply_propval *val); @@ -505,9 +505,10 @@ int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg, void smblib_suspend_on_debug_battery(struct smb_charger *chg); int smblib_rerun_apsd_if_required(struct smb_charger *chg); int smblib_get_prop_fcc_delta(struct smb_charger *chg, - union power_supply_propval *val); + union power_supply_propval *val); int smblib_icl_override(struct smb_charger *chg, bool override); int smblib_dp_dm(struct smb_charger *chg, int val); +int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable); int smblib_rerun_aicl(struct smb_charger *chg); int smblib_set_icl_current(struct smb_charger *chg, int icl_ua); int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua); diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 335b160e24a5..e7ca1e3fb108 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -215,6 +215,7 @@ static enum power_supply_property smb138x_usb_props[] = { POWER_SUPPLY_PROP_TYPEC_MODE, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, + POWER_SUPPLY_PROP_SDP_CURRENT_MAX, }; static int smb138x_usb_get_prop(struct power_supply *psy, @@ -242,7 +243,7 @@ static int smb138x_usb_get_prop(struct power_supply *psy, rc = smblib_get_prop_usb_voltage_now(chg, val); break; case POWER_SUPPLY_PROP_CURRENT_MAX: - rc = smblib_get_prop_usb_current_max(chg, val); + val->intval = get_effective_result(chg->usb_icl_votable); break; case POWER_SUPPLY_PROP_TYPE: val->intval = chg->usb_psy_desc.type; @@ -256,6 +257,10 @@ static int smb138x_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION: rc = smblib_get_prop_typec_cc_orientation(chg, val); break; + case POWER_SUPPLY_PROP_SDP_CURRENT_MAX: + val->intval = get_client_vote(chg->usb_icl_votable, + USB_PSY_VOTER); + break; default: pr_err("get prop %d is not supported\n", prop); return -EINVAL; @@ -278,18 +283,12 @@ static int smb138x_usb_set_prop(struct power_supply *psy, int rc = 0; switch (prop) { - case POWER_SUPPLY_PROP_VOLTAGE_MIN: - rc = smblib_set_prop_usb_voltage_min(chg, val); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - rc = smblib_set_prop_usb_voltage_max(chg, val); - break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - rc = smblib_set_prop_usb_current_max(chg, val); - break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: rc = smblib_set_prop_typec_power_role(chg, val); break; + case POWER_SUPPLY_PROP_SDP_CURRENT_MAX: + rc = smblib_set_prop_sdp_current_max(chg, val); + break; default: pr_err("set prop %d is not supported\n", prop); return -EINVAL; @@ -301,13 +300,6 @@ static int smb138x_usb_set_prop(struct power_supply *psy, static int smb138x_usb_prop_is_writeable(struct power_supply *psy, enum power_supply_property prop) { - switch (prop) { - case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: - return 1; - default: - break; - } - return 0; } @@ -845,6 +837,13 @@ static int smb138x_init_slave_hw(struct smb138x *chip) } } + /* configure to a fixed 700khz freq to avoid tdie errors */ + rc = smblib_set_charge_param(chg, &chg->param.freq_buck, 700); + if (rc < 0) { + pr_err("Couldn't configure 700Khz switch freq rc=%d\n", rc); + return rc; + } + /* enable watchdog bark and bite interrupts, and disable the watchdog */ rc = smblib_masked_write(chg, WD_CFG_REG, WDOG_TIMER_EN_BIT | WDOG_TIMER_EN_ON_PLUGIN_BIT | BITE_WDOG_INT_EN_BIT @@ -864,6 +863,13 @@ static int smb138x_init_slave_hw(struct smb138x *chip) return rc; } + /* Disable OTG */ + rc = smblib_masked_write(chg, CMD_OTG_REG, OTG_EN_BIT, 0); + if (rc < 0) { + pr_err("Couldn't disable OTG rc=%d\n", rc); + return rc; + } + /* suspend parallel charging */ rc = smb138x_set_parallel_suspend(chip, true); if (rc < 0) { @@ -961,6 +967,20 @@ static int smb138x_init_hw(struct smb138x *chip) chg->dcp_icl_ua = chip->dt.usb_icl_ua; + /* Disable OTG */ + rc = smblib_masked_write(chg, CMD_OTG_REG, OTG_EN_BIT, 0); + if (rc < 0) { + pr_err("Couldn't disable OTG rc=%d\n", rc); + return rc; + } + + /* Unsuspend USB input */ + rc = smblib_masked_write(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, 0); + if (rc < 0) { + pr_err("Couldn't unsuspend USB, rc=%d\n", rc); + return rc; + } + /* configure to a fixed 700khz freq to avoid tdie errors */ rc = smblib_set_charge_param(chg, &chg->param.freq_buck, 700); if (rc < 0) { @@ -1601,14 +1621,33 @@ static int smb138x_remove(struct platform_device *pdev) return 0; } +static void smb138x_shutdown(struct platform_device *pdev) +{ + struct smb138x *chip = platform_get_drvdata(pdev); + struct smb_charger *chg = &chip->chg; + int rc; + + /* Suspend charging */ + rc = smb138x_set_parallel_suspend(chip, true); + if (rc < 0) + pr_err("Couldn't suspend charging rc=%d\n", rc); + + /* Disable OTG */ + rc = smblib_masked_write(chg, CMD_OTG_REG, OTG_EN_BIT, 0); + if (rc < 0) + pr_err("Couldn't disable OTG rc=%d\n", rc); + +} + static struct platform_driver smb138x_driver = { .driver = { .name = "qcom,smb138x-charger", .owner = THIS_MODULE, .of_match_table = match_table, }, - .probe = smb138x_probe, - .remove = smb138x_remove, + .probe = smb138x_probe, + .remove = smb138x_remove, + .shutdown = smb138x_shutdown, }; module_platform_driver(smb138x_driver); diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c new file mode 100644 index 000000000000..06ecc7ea6e8a --- /dev/null +++ b/drivers/power/supply/qcom/step-chg-jeita.c @@ -0,0 +1,501 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define pr_fmt(fmt) "QCOM-STEPCHG: %s: " fmt, __func__ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/power_supply.h> +#include <linux/slab.h> +#include <linux/pmic-voter.h> +#include "step-chg-jeita.h" + +#define MAX_STEP_CHG_ENTRIES 8 +#define STEP_CHG_VOTER "STEP_CHG_VOTER" +#define JEITA_VOTER "JEITA_VOTER" + +#define is_between(left, right, value) \ + (((left) >= (right) && (left) >= (value) \ + && (value) >= (right)) \ + || ((left) <= (right) && (left) <= (value) \ + && (value) <= (right))) + +struct range_data { + u32 low_threshold; + u32 high_threshold; + u32 value; +}; + +struct step_chg_cfg { + u32 psy_prop; + char *prop_name; + int hysteresis; + struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES]; +}; + +struct jeita_fcc_cfg { + u32 psy_prop; + char *prop_name; + int hysteresis; + struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES]; +}; + +struct jeita_fv_cfg { + u32 psy_prop; + char *prop_name; + int hysteresis; + struct range_data fv_cfg[MAX_STEP_CHG_ENTRIES]; +}; + +struct step_chg_info { + ktime_t step_last_update_time; + ktime_t jeita_last_update_time; + bool step_chg_enable; + bool sw_jeita_enable; + int jeita_fcc_index; + int jeita_fv_index; + int step_index; + + struct votable *fcc_votable; + struct votable *fv_votable; + struct wakeup_source *step_chg_ws; + struct power_supply *batt_psy; + struct delayed_work status_change_work; + struct notifier_block nb; +}; + +static struct step_chg_info *the_chip; + +#define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */ + +/* + * Step Charging Configuration + * Update the table based on the battery profile + * Supports VBATT and SOC based source + * range data must be in increasing ranges and shouldn't overlap + */ +static struct step_chg_cfg step_chg_config = { + .psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW, + .prop_name = "VBATT", + .hysteresis = 100000, /* 100mV */ + .fcc_cfg = { + /* VBAT_LOW VBAT_HIGH FCC */ + {3600000, 4000000, 3000000}, + {4001000, 4200000, 2800000}, + {4201000, 4400000, 2000000}, + }, + /* + * SOC STEP-CHG configuration example. + * + * .psy_prop = POWER_SUPPLY_PROP_CAPACITY, + * .prop_name = "SOC", + * .fcc_cfg = { + * //SOC_LOW SOC_HIGH FCC + * {20, 70, 3000000}, + * {70, 90, 2750000}, + * {90, 100, 2500000}, + * }, + */ +}; + +/* + * Jeita Charging Configuration + * Update the table based on the battery profile + * Please ensure that the TEMP ranges are programmed in the hw so that + * an interrupt is issued and a consequent psy changed will cause us to + * react immediately. + * range data must be in increasing ranges and shouldn't overlap. + * Gaps are okay + */ +static struct jeita_fcc_cfg jeita_fcc_config = { + .psy_prop = POWER_SUPPLY_PROP_TEMP, + .prop_name = "BATT_TEMP", + .hysteresis = 10, /* 1degC hysteresis */ + .fcc_cfg = { + /* TEMP_LOW TEMP_HIGH FCC */ + {0, 100, 600000}, + {101, 200, 2000000}, + {201, 450, 3000000}, + {451, 550, 600000}, + }, +}; + +static struct jeita_fv_cfg jeita_fv_config = { + .psy_prop = POWER_SUPPLY_PROP_TEMP, + .prop_name = "BATT_TEMP", + .hysteresis = 10, /* 1degC hysteresis */ + .fv_cfg = { + /* TEMP_LOW TEMP_HIGH FCC */ + {0, 100, 4200000}, + {101, 450, 4400000}, + {451, 550, 4200000}, + }, +}; + +static bool is_batt_available(struct step_chg_info *chip) +{ + if (!chip->batt_psy) + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) + return false; + + return true; +} + +static int get_val(struct range_data *range, int hysteresis, int current_index, + int threshold, + int *new_index, int *val) +{ + int i; + + *new_index = -EINVAL; + /* first find the matching index without hysteresis */ + for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++) + if (is_between(range[i].low_threshold, + range[i].high_threshold, threshold)) { + *new_index = i; + *val = range[i].value; + } + + /* if nothing was found, return -ENODATA */ + if (*new_index == -EINVAL) + return -ENODATA; + /* + * If we don't have a current_index return this + * newfound value. There is no hysterisis from out of range + * to in range transition + */ + if (current_index == -EINVAL) + return 0; + + /* + * Check for hysteresis if it in the neighbourhood + * of our current index. + */ + if (*new_index == current_index + 1) { + if (threshold < range[*new_index].low_threshold + hysteresis) { + /* + * Stay in the current index, threshold is not higher + * by hysteresis amount + */ + *new_index = current_index; + *val = range[current_index].value; + } + } else if (*new_index == current_index - 1) { + if (threshold > range[*new_index].high_threshold - hysteresis) { + /* + * stay in the current index, threshold is not lower + * by hysteresis amount + */ + *new_index = current_index; + *val = range[current_index].value; + } + } + return 0; +} + +static int handle_step_chg_config(struct step_chg_info *chip) +{ + union power_supply_propval pval = {0, }; + int rc = 0, fcc_ua = 0; + u64 elapsed_us; + + elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time); + if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) + goto reschedule; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, &pval); + if (rc < 0) + chip->step_chg_enable = 0; + else + chip->step_chg_enable = pval.intval; + + if (!chip->step_chg_enable) { + if (chip->fcc_votable) + vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0); + goto update_time; + } + + rc = power_supply_get_property(chip->batt_psy, + step_chg_config.psy_prop, &pval); + if (rc < 0) { + pr_err("Couldn't read %s property rc=%d\n", + step_chg_config.prop_name, rc); + return rc; + } + + rc = get_val(step_chg_config.fcc_cfg, step_chg_config.hysteresis, + chip->step_index, + pval.intval, + &chip->step_index, + &fcc_ua); + if (rc < 0) { + /* remove the vote if no step-based fcc is found */ + if (chip->fcc_votable) + vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0); + goto update_time; + } + + if (!chip->fcc_votable) + chip->fcc_votable = find_votable("FCC"); + if (!chip->fcc_votable) + return -EINVAL; + + vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua); + + pr_debug("%s = %d Step-FCC = %duA\n", + step_chg_config.prop_name, pval.intval, fcc_ua); + +update_time: + chip->step_last_update_time = ktime_get(); + return 0; + +reschedule: + /* reschedule 1000uS after the remaining time */ + return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000); +} + +static int handle_jeita(struct step_chg_info *chip) +{ + union power_supply_propval pval = {0, }; + int rc = 0, fcc_ua = 0, fv_uv = 0; + u64 elapsed_us; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, &pval); + if (rc < 0) + chip->sw_jeita_enable = 0; + else + chip->sw_jeita_enable = pval.intval; + + if (!chip->sw_jeita_enable) { + if (chip->fcc_votable) + vote(chip->fcc_votable, JEITA_VOTER, false, 0); + if (chip->fv_votable) + vote(chip->fv_votable, JEITA_VOTER, false, 0); + return 0; + } + + elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time); + if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) + goto reschedule; + + rc = power_supply_get_property(chip->batt_psy, + jeita_fcc_config.psy_prop, &pval); + if (rc < 0) { + pr_err("Couldn't read %s property rc=%d\n", + step_chg_config.prop_name, rc); + return rc; + } + + rc = get_val(jeita_fcc_config.fcc_cfg, jeita_fcc_config.hysteresis, + chip->jeita_fcc_index, + pval.intval, + &chip->jeita_fcc_index, + &fcc_ua); + if (rc < 0) { + /* remove the vote if no step-based fcc is found */ + if (chip->fcc_votable) + vote(chip->fcc_votable, JEITA_VOTER, false, 0); + goto update_time; + } + + if (!chip->fcc_votable) + chip->fcc_votable = find_votable("FCC"); + if (!chip->fcc_votable) + /* changing FCC is a must */ + return -EINVAL; + + vote(chip->fcc_votable, JEITA_VOTER, true, fcc_ua); + + rc = get_val(jeita_fv_config.fv_cfg, jeita_fv_config.hysteresis, + chip->jeita_fv_index, + pval.intval, + &chip->jeita_fv_index, + &fv_uv); + if (rc < 0) { + /* remove the vote if no step-based fcc is found */ + if (chip->fv_votable) + vote(chip->fv_votable, JEITA_VOTER, false, 0); + goto update_time; + } + + chip->fv_votable = find_votable("FV"); + if (!chip->fv_votable) + goto update_time; + + vote(chip->fv_votable, JEITA_VOTER, true, fv_uv); + + pr_debug("%s = %d FCC = %duA FV = %duV\n", + step_chg_config.prop_name, pval.intval, fcc_ua, fv_uv); + +update_time: + chip->jeita_last_update_time = ktime_get(); + return 0; + +reschedule: + /* reschedule 1000uS after the remaining time */ + return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000); +} + +static void status_change_work(struct work_struct *work) +{ + struct step_chg_info *chip = container_of(work, + struct step_chg_info, status_change_work.work); + int rc = 0; + int reschedule_us; + int reschedule_jeita_work_us = 0; + int reschedule_step_work_us = 0; + + if (!is_batt_available(chip)) + return; + + /* skip elapsed_us debounce for handling battery temperature */ + rc = handle_jeita(chip); + if (rc > 0) + reschedule_jeita_work_us = rc; + else if (rc < 0) + pr_err("Couldn't handle sw jeita rc = %d\n", rc); + + rc = handle_step_chg_config(chip); + if (rc > 0) + reschedule_step_work_us = rc; + if (rc < 0) + pr_err("Couldn't handle step rc = %d\n", rc); + + reschedule_us = min(reschedule_jeita_work_us, reschedule_step_work_us); + if (reschedule_us == 0) + __pm_relax(chip->step_chg_ws); + else + schedule_delayed_work(&chip->status_change_work, + usecs_to_jiffies(reschedule_us)); +} + +static int step_chg_notifier_call(struct notifier_block *nb, + unsigned long ev, void *v) +{ + struct power_supply *psy = v; + struct step_chg_info *chip = container_of(nb, struct step_chg_info, nb); + + if (ev != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if ((strcmp(psy->desc->name, "battery") == 0)) { + __pm_stay_awake(chip->step_chg_ws); + schedule_delayed_work(&chip->status_change_work, 0); + } + + return NOTIFY_OK; +} + +static int step_chg_register_notifier(struct step_chg_info *chip) +{ + int rc; + + chip->nb.notifier_call = step_chg_notifier_call; + rc = power_supply_reg_notifier(&chip->nb); + if (rc < 0) { + pr_err("Couldn't register psy notifier rc = %d\n", rc); + return rc; + } + + return 0; +} + +int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable) +{ + int rc; + struct step_chg_info *chip; + + if (the_chip) { + pr_err("Already initialized\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->step_chg_ws = wakeup_source_register("qcom-step-chg"); + if (!chip->step_chg_ws) { + rc = -EINVAL; + goto cleanup; + } + + chip->step_chg_enable = step_chg_enable; + chip->sw_jeita_enable = sw_jeita_enable; + + chip->step_index = -EINVAL; + chip->jeita_fcc_index = -EINVAL; + chip->jeita_fv_index = -EINVAL; + + if (step_chg_enable && (!step_chg_config.psy_prop || + !step_chg_config.prop_name)) { + /* fail if step-chg configuration is invalid */ + pr_err("Step-chg configuration not defined - fail\n"); + rc = -ENODATA; + goto release_wakeup_source; + } + + if (sw_jeita_enable && (!jeita_fcc_config.psy_prop || + !jeita_fcc_config.prop_name)) { + /* fail if step-chg configuration is invalid */ + pr_err("Jeita TEMP configuration not defined - fail\n"); + rc = -ENODATA; + goto release_wakeup_source; + } + + if (sw_jeita_enable && (!jeita_fv_config.psy_prop || + !jeita_fv_config.prop_name)) { + /* fail if step-chg configuration is invalid */ + pr_err("Jeita TEMP configuration not defined - fail\n"); + rc = -ENODATA; + goto release_wakeup_source; + } + + INIT_DELAYED_WORK(&chip->status_change_work, status_change_work); + + rc = step_chg_register_notifier(chip); + if (rc < 0) { + pr_err("Couldn't register psy notifier rc = %d\n", rc); + goto release_wakeup_source; + } + + the_chip = chip; + + if (step_chg_enable) + pr_info("Step charging enabled. Using %s source\n", + step_chg_config.prop_name); + + return 0; + +release_wakeup_source: + wakeup_source_unregister(chip->step_chg_ws); +cleanup: + kfree(chip); + return rc; +} + +void qcom_step_chg_deinit(void) +{ + struct step_chg_info *chip = the_chip; + + if (!chip) + return; + + cancel_delayed_work_sync(&chip->status_change_work); + power_supply_unreg_notifier(&chip->nb); + wakeup_source_unregister(chip->step_chg_ws); + the_chip = NULL; + kfree(chip); +} diff --git a/drivers/power/supply/qcom/step-chg-jeita.h b/drivers/power/supply/qcom/step-chg-jeita.h new file mode 100644 index 000000000000..53335c3c2c70 --- /dev/null +++ b/drivers/power/supply/qcom/step-chg-jeita.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __STEP_CHG_H__ +#define __STEP_CHG_H__ +int qcom_step_chg_init(bool, bool); +void qcom_step_chg_deinit(void); +#endif /* __STEP_CHG_H__ */ diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c index e46b1d583f40..2531b74b4588 100644 --- a/drivers/pwm/pwm-qpnp.c +++ b/drivers/pwm/pwm-qpnp.c @@ -328,6 +328,7 @@ struct qpnp_pwm_chip { bool enabled; struct _qpnp_pwm_config pwm_config; struct qpnp_lpg_config lpg_config; + enum pm_pwm_mode pwm_mode; spinlock_t lpg_lock; enum qpnp_lpg_revision revision; u8 sub_type; @@ -1314,12 +1315,10 @@ after_table_write: return rc; } +/* lpg_lock should be held while calling _pwm_enable() */ static int _pwm_enable(struct qpnp_pwm_chip *chip) { int rc = 0; - unsigned long flags; - - spin_lock_irqsave(&chip->lpg_lock, flags); if (QPNP_IS_PWM_CONFIG_SELECTED( chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]) || @@ -1332,8 +1331,21 @@ static int _pwm_enable(struct qpnp_pwm_chip *chip) if (!rc) chip->enabled = true; - spin_unlock_irqrestore(&chip->lpg_lock, flags); + return rc; +} + +/* lpg_lock should be held while calling _pwm_change_mode() */ +static int _pwm_change_mode(struct qpnp_pwm_chip *chip, enum pm_pwm_mode mode) +{ + int rc; + if (mode == PM_PWM_MODE_LPG) + rc = qpnp_configure_lpg_control(chip); + else + rc = qpnp_configure_pwm_control(chip); + + if (rc) + pr_err("Failed to change the mode\n"); return rc; } @@ -1410,11 +1422,15 @@ static int qpnp_pwm_enable(struct pwm_chip *pwm_chip, { int rc; struct qpnp_pwm_chip *chip = qpnp_pwm_from_pwm_chip(pwm_chip); + unsigned long flags; + spin_lock_irqsave(&chip->lpg_lock, flags); rc = _pwm_enable(chip); if (rc) pr_err("Failed to enable PWM channel: %d\n", chip->channel_id); + spin_unlock_irqrestore(&chip->lpg_lock, flags); + return rc; } @@ -1452,20 +1468,6 @@ static void qpnp_pwm_disable(struct pwm_chip *pwm_chip, chip->channel_id); } -static int _pwm_change_mode(struct qpnp_pwm_chip *chip, enum pm_pwm_mode mode) -{ - int rc; - - if (mode) - rc = qpnp_configure_lpg_control(chip); - else - rc = qpnp_configure_pwm_control(chip); - - if (rc) - pr_err("Failed to change the mode\n"); - return rc; -} - /** * pwm_change_mode - Change the PWM mode configuration * @pwm: the PWM device @@ -1490,7 +1492,22 @@ int pwm_change_mode(struct pwm_device *pwm, enum pm_pwm_mode mode) chip = qpnp_pwm_from_pwm_dev(pwm); spin_lock_irqsave(&chip->lpg_lock, flags); - rc = _pwm_change_mode(chip, mode); + if (chip->pwm_mode != mode) { + rc = _pwm_change_mode(chip, mode); + if (rc) { + pr_err("Failed to change mode: %d, rc=%d\n", mode, rc); + goto unlock; + } + chip->pwm_mode = mode; + if (chip->enabled) { + rc = _pwm_enable(chip); + if (rc) { + pr_err("Failed to enable PWM, rc=%d\n", rc); + goto unlock; + } + } + } +unlock: spin_unlock_irqrestore(&chip->lpg_lock, flags); return rc; @@ -1894,7 +1911,7 @@ out: static int qpnp_parse_dt_config(struct platform_device *pdev, struct qpnp_pwm_chip *chip) { - int rc, enable, lut_entry_size, list_size, i; + int rc, mode, lut_entry_size, list_size, i; const char *label; const __be32 *prop; u32 size; @@ -2078,18 +2095,20 @@ static int qpnp_parse_dt_config(struct platform_device *pdev, } } - rc = of_property_read_u32(of_node, "qcom,mode-select", &enable); + rc = of_property_read_u32(of_node, "qcom,mode-select", &mode); if (rc) goto read_opt_props; - if ((enable == PM_PWM_MODE_PWM && found_pwm_subnode == 0) || - (enable == PM_PWM_MODE_LPG && found_lpg_subnode == 0)) { + if (mode > PM_PWM_MODE_LPG || + (mode == PM_PWM_MODE_PWM && found_pwm_subnode == 0) || + (mode == PM_PWM_MODE_LPG && found_lpg_subnode == 0)) { dev_err(&pdev->dev, "%s: Invalid mode select\n", __func__); rc = -EINVAL; goto out; } - _pwm_change_mode(chip, enable); + chip->pwm_mode = mode; + _pwm_change_mode(chip, mode); _pwm_enable(chip); read_opt_props: diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c index ecf7885a4bff..4d6d63e6d887 100644 --- a/drivers/regulator/cprh-kbss-regulator.c +++ b/drivers/regulator/cprh-kbss-regulator.c @@ -80,7 +80,7 @@ struct cprh_kbss_fuses { * Fuse combos 24 - 31 map to CPR fusing revision 0 - 7 with speed bin fuse = 3. */ #define CPRH_MSM8998_KBSS_FUSE_COMBO_COUNT 32 -#define CPRH_SDM660_KBSS_FUSE_COMBO_COUNT 16 +#define CPRH_SDM660_KBSS_FUSE_COMBO_COUNT 32 #define CPRH_SDM630_KBSS_FUSE_COMBO_COUNT 24 /* @@ -1069,6 +1069,12 @@ static int cprh_kbss_calculate_open_loop_voltages(struct cpr3_regulator *vreg) CPRH_KBSS_FUSE_STEP_VOLT, fuse->init_voltage[i], CPRH_KBSS_VOLTAGE_FUSE_SIZE); + /* SDM660 speed bin #3 does not support TURBO_L1/L2 */ + if (soc_revision == SDM660_SOC_ID && vreg->speed_bin_fuse == 3 + && (id == CPRH_KBSS_PERFORMANCE_CLUSTER_ID) + && (i == CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO_L2)) + continue; + /* Log fused open-loop voltage values for debugging purposes. */ cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", corner_name[i], fuse_volt[i]); @@ -1615,6 +1621,11 @@ static int cprh_kbss_calculate_target_quotients(struct cpr3_regulator *vreg) CPRH_SDM660_PERF_KBSS_FUSE_CORNER_SVS; highest_fuse_corner = CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO_L2; + + /* speed-bin 3 does not have Turbo_L2 fuse */ + if (vreg->speed_bin_fuse == 3) + highest_fuse_corner = + CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO; } break; case SDM630_SOC_ID: diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c index 724e8a4c00da..27f2d9851c2a 100644 --- a/drivers/regulator/qpnp-lcdb-regulator.c +++ b/drivers/regulator/qpnp-lcdb-regulator.c @@ -147,6 +147,8 @@ #define MIN_SOFT_START_US 0 #define MAX_SOFT_START_US 2000 +#define BST_HEADROOM_DEFAULT_MV 200 + struct ldo_regulator { struct regulator_desc rdesc; struct regulator_dev *rdev; @@ -187,6 +189,7 @@ struct bst_params { int soft_start_us; int vreg_ok_dbc_us; int voltage_mv; + u16 headroom_mv; }; struct qpnp_lcdb { @@ -853,28 +856,40 @@ irq_handled: #define VOLTAGE_STEP_50_MV 50 #define VOLTAGE_STEP_50MV_OFFSET 0xA static int qpnp_lcdb_set_bst_voltage(struct qpnp_lcdb *lcdb, - int voltage_mv) + int voltage_mv, u8 type) { int rc = 0; u8 val = 0; + int bst_voltage_mv; + struct ldo_regulator *ldo = &lcdb->ldo; + struct ncp_regulator *ncp = &lcdb->ncp; + struct bst_params *bst = &lcdb->bst; + + /* Vout_Boost = headroom_mv + max( Vout_LDO, abs (Vout_NCP)) */ + bst_voltage_mv = max(voltage_mv, max(ldo->voltage_mv, ncp->voltage_mv)); + bst_voltage_mv += bst->headroom_mv; + + if (bst_voltage_mv < MIN_BST_VOLTAGE_MV) + bst_voltage_mv = MIN_BST_VOLTAGE_MV; + else if (bst_voltage_mv > MAX_BST_VOLTAGE_MV) + bst_voltage_mv = MAX_BST_VOLTAGE_MV; + + if (bst_voltage_mv != bst->voltage_mv) { + val = DIV_ROUND_UP(bst_voltage_mv - MIN_BST_VOLTAGE_MV, + VOLTAGE_STEP_50_MV); - if (voltage_mv < MIN_BST_VOLTAGE_MV) - voltage_mv = MIN_BST_VOLTAGE_MV; - else if (voltage_mv > MAX_BST_VOLTAGE_MV) - voltage_mv = MAX_BST_VOLTAGE_MV; - - val = DIV_ROUND_UP(voltage_mv - MIN_BST_VOLTAGE_MV, - VOLTAGE_STEP_50_MV); - - rc = qpnp_lcdb_masked_write(lcdb, lcdb->base + - LCDB_BST_OUTPUT_VOLTAGE_REG, - SET_OUTPUT_VOLTAGE_MASK, val); - if (rc < 0) - pr_err("Failed to set boost voltage %d mv rc=%d\n", - voltage_mv, rc); - else - pr_debug("Boost voltage set = %d mv (0x%02x = 0x%02x)\n", - voltage_mv, LCDB_BST_OUTPUT_VOLTAGE_REG, val); + rc = qpnp_lcdb_masked_write(lcdb, lcdb->base + + LCDB_BST_OUTPUT_VOLTAGE_REG, + SET_OUTPUT_VOLTAGE_MASK, val); + if (rc < 0) { + pr_err("Failed to set boost voltage %d mv rc=%d\n", + bst_voltage_mv, rc); + } else { + pr_debug("Boost voltage set = %d mv (0x%02x = 0x%02x)\n", + bst_voltage_mv, LCDB_BST_OUTPUT_VOLTAGE_REG, val); + bst->voltage_mv = bst_voltage_mv; + } + } return rc; } @@ -905,25 +920,16 @@ static int qpnp_lcdb_set_voltage(struct qpnp_lcdb *lcdb, u16 offset = LCDB_LDO_OUTPUT_VOLTAGE_REG; u8 val = 0; - if (type == BST) - return qpnp_lcdb_set_bst_voltage(lcdb, voltage_mv); - - if (type == NCP) - offset = LCDB_NCP_OUTPUT_VOLTAGE_REG; - if (!is_between(voltage_mv, MIN_VOLTAGE_MV, MAX_VOLTAGE_MV)) { pr_err("Invalid voltage %dmv (min=%d max=%d)\n", voltage_mv, MIN_VOLTAGE_MV, MAX_VOLTAGE_MV); return -EINVAL; } - /* Change the BST voltage to LDO + 100mV */ - if (type == LDO) { - rc = qpnp_lcdb_set_bst_voltage(lcdb, voltage_mv + 100); - if (rc < 0) { - pr_err("Failed to set boost voltage rc=%d\n", rc); - return rc; - } + rc = qpnp_lcdb_set_bst_voltage(lcdb, voltage_mv, type); + if (rc < 0) { + pr_err("Failed to set boost voltage rc=%d\n", rc); + return rc; } /* Below logic is only valid for LDO and NCP type */ @@ -936,6 +942,9 @@ static int qpnp_lcdb_set_voltage(struct qpnp_lcdb *lcdb, val += VOLTAGE_STEP_50MV_OFFSET; } + if (type == NCP) + offset = LCDB_NCP_OUTPUT_VOLTAGE_REG; + rc = qpnp_lcdb_masked_write(lcdb, lcdb->base + offset, SET_OUTPUT_VOLTAGE_MASK, val); if (rc < 0) @@ -1058,6 +1067,8 @@ static int qpnp_lcdb_ldo_regulator_set_voltage(struct regulator_dev *rdev, rc = qpnp_lcdb_set_voltage(lcdb, min_uV / 1000, LDO); if (rc < 0) pr_err("Failed to set LDO voltage rc=%c\n", rc); + else + lcdb->ldo.voltage_mv = min_uV / 1000; return rc; } @@ -1129,6 +1140,8 @@ static int qpnp_lcdb_ncp_regulator_set_voltage(struct regulator_dev *rdev, rc = qpnp_lcdb_set_voltage(lcdb, min_uV / 1000, NCP); if (rc < 0) pr_err("Failed to set LDO voltage rc=%c\n", rc); + else + lcdb->ncp.voltage_mv = min_uV / 1000; return rc; } @@ -1389,6 +1402,12 @@ static int qpnp_lcdb_bst_dt_init(struct qpnp_lcdb *lcdb) return -EINVAL; } + /* Boost head room configuration */ + of_property_read_u16(node, "qcom,bst-headroom-mv", + &lcdb->bst.headroom_mv); + if (lcdb->bst.headroom_mv < BST_HEADROOM_DEFAULT_MV) + lcdb->bst.headroom_mv = BST_HEADROOM_DEFAULT_MV; + return 0; } @@ -1695,6 +1714,9 @@ static int qpnp_lcdb_init_bst(struct qpnp_lcdb *lcdb) } lcdb->bst.soft_start_us = (val & SOFT_START_MASK) * 200 + 200; + if (!lcdb->bst.headroom_mv) + lcdb->bst.headroom_mv = BST_HEADROOM_DEFAULT_MV; + return 0; } diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index ce129e595b55..5c935847599c 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -248,6 +248,7 @@ struct fnic { struct completion *remove_wait; /* device remove thread blocks */ atomic_t in_flight; /* io counter */ + bool internal_reset_inprogress; u32 _reserved; /* fill hole */ unsigned long state_flags; /* protected by host lock */ enum fnic_state state; diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 266b909fe854..82e4bc8c11c5 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -2533,6 +2533,19 @@ int fnic_host_reset(struct scsi_cmnd *sc) unsigned long wait_host_tmo; struct Scsi_Host *shost = sc->device->host; struct fc_lport *lp = shost_priv(shost); + struct fnic *fnic = lport_priv(lp); + unsigned long flags; + + spin_lock_irqsave(&fnic->fnic_lock, flags); + if (fnic->internal_reset_inprogress == 0) { + fnic->internal_reset_inprogress = 1; + } else { + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, + "host reset in progress skipping another host reset\n"); + return SUCCESS; + } + spin_unlock_irqrestore(&fnic->fnic_lock, flags); /* * If fnic_reset is successful, wait for fabric login to complete @@ -2553,6 +2566,9 @@ int fnic_host_reset(struct scsi_cmnd *sc) } } + spin_lock_irqsave(&fnic->fnic_lock, flags); + fnic->internal_reset_inprogress = 0; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); return ret; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 5b2c37f1e908..9b5367294116 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -4981,15 +4981,14 @@ _base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, static int _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) { - int r, i; + int r, i, index; unsigned long flags; u32 reply_address; u16 smid; struct _tr_list *delayed_tr, *delayed_tr_next; u8 hide_flag; struct adapter_reply_queue *reply_q; - long reply_post_free; - u32 reply_post_free_sz, index = 0; + Mpi2ReplyDescriptorsUnion_t *reply_post_free_contig; dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); @@ -5061,27 +5060,27 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) _base_assign_reply_queues(ioc); /* initialize Reply Post Free Queue */ - reply_post_free_sz = ioc->reply_post_queue_depth * - sizeof(Mpi2DefaultReplyDescriptor_t); - reply_post_free = (long)ioc->reply_post[index].reply_post_free; + index = 0; + reply_post_free_contig = ioc->reply_post[0].reply_post_free; list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { + /* + * If RDPQ is enabled, switch to the next allocation. + * Otherwise advance within the contiguous region. + */ + if (ioc->rdpq_array_enable) { + reply_q->reply_post_free = + ioc->reply_post[index++].reply_post_free; + } else { + reply_q->reply_post_free = reply_post_free_contig; + reply_post_free_contig += ioc->reply_post_queue_depth; + } + reply_q->reply_post_host_index = 0; - reply_q->reply_post_free = (Mpi2ReplyDescriptorsUnion_t *) - reply_post_free; for (i = 0; i < ioc->reply_post_queue_depth; i++) reply_q->reply_post_free[i].Words = cpu_to_le64(ULLONG_MAX); if (!_base_is_controller_msix_enabled(ioc)) goto skip_init_reply_post_free_queue; - /* - * If RDPQ is enabled, switch to the next allocation. - * Otherwise advance within the contiguous region. - */ - if (ioc->rdpq_array_enable) - reply_post_free = (long) - ioc->reply_post[++index].reply_post_free; - else - reply_post_free += reply_post_free_sz; } skip_init_reply_post_free_queue: diff --git a/drivers/scsi/snic/snic_main.c b/drivers/scsi/snic/snic_main.c index 2b3c25371d76..8175f997e82c 100644 --- a/drivers/scsi/snic/snic_main.c +++ b/drivers/scsi/snic/snic_main.c @@ -584,6 +584,7 @@ snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!pool) { SNIC_HOST_ERR(shost, "dflt sgl pool creation failed\n"); + ret = -ENOMEM; goto err_free_res; } @@ -594,6 +595,7 @@ snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!pool) { SNIC_HOST_ERR(shost, "max sgl pool creation failed\n"); + ret = -ENOMEM; goto err_free_dflt_sgl_pool; } @@ -604,6 +606,7 @@ snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!pool) { SNIC_HOST_ERR(shost, "snic tmreq info pool creation failed.\n"); + ret = -ENOMEM; goto err_free_max_sgl_pool; } diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index aae796678ffe..47106f937371 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -2416,7 +2416,8 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, */ static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg) { - int ret = 0, scm_ret = 0; + int ret = 0; + u64 scm_ret = 0; struct ufs_qcom_host *host = ufshcd_get_variant(hba); /* scm command buffer structrue */ @@ -2457,7 +2458,7 @@ static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg) cbuf.device_id = UFS_TZ_DEV_ID; ret = scm_restore_sec_cfg(cbuf.device_id, cbuf.spare, &scm_ret); if (ret || scm_ret) { - dev_dbg(hba->dev, "%s: failed, ret %d scm_ret %d\n", + dev_dbg(hba->dev, "%s: failed, ret %d scm_ret %llu\n", __func__, ret, scm_ret); if (!ret) ret = scm_ret; @@ -2466,7 +2467,7 @@ static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg) } out: - dev_dbg(hba->dev, "%s: ip: restore_sec_cfg %d, op: restore_sec_cfg %d, ret %d scm_ret %d\n", + dev_dbg(hba->dev, "%s: ip: restore_sec_cfg %d, op: restore_sec_cfg %d, ret %d scm_ret %llu\n", __func__, restore_sec_cfg, host->sec_cfg_updated, ret, scm_ret); return ret; } diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index a2f2cc0c2c51..c23023f43d30 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1483,7 +1483,7 @@ start: hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); - queue_work(hba->clk_gating.ungating_workq, + queue_work(hba->clk_gating.clk_gating_workq, &hba->clk_gating.ungate_work); /* * fall through to check if we should wait for this @@ -1751,7 +1751,8 @@ static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler( struct ufs_hba *hba = container_of(timer, struct ufs_hba, clk_gating.gate_hrtimer); - schedule_work(&hba->clk_gating.gate_work); + queue_work(hba->clk_gating.clk_gating_workq, + &hba->clk_gating.gate_work); return HRTIMER_NORESTART; } @@ -1759,7 +1760,7 @@ static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler( static void ufshcd_init_clk_gating(struct ufs_hba *hba) { struct ufs_clk_gating *gating = &hba->clk_gating; - char wq_name[sizeof("ufs_clk_ungating_00")]; + char wq_name[sizeof("ufs_clk_gating_00")]; hba->clk_gating.state = CLKS_ON; @@ -1788,9 +1789,10 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) hrtimer_init(&gating->gate_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); gating->gate_hrtimer.function = ufshcd_clkgate_hrtimer_handler; - snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_ungating_%d", + snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_gating_%d", hba->host->host_no); - hba->clk_gating.ungating_workq = create_singlethread_workqueue(wq_name); + hba->clk_gating.clk_gating_workq = + create_singlethread_workqueue(wq_name); gating->is_enabled = true; @@ -1854,7 +1856,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) device_remove_file(hba->dev, &hba->clk_gating.enable_attr); ufshcd_cancel_gate_work(hba); cancel_work_sync(&hba->clk_gating.ungate_work); - destroy_workqueue(hba->clk_gating.ungating_workq); + destroy_workqueue(hba->clk_gating.clk_gating_workq); } static void ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba, u32 delay) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index d66205ff9f5d..da3ad78d3405 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -431,7 +431,7 @@ struct ufs_clk_gating { struct device_attribute enable_attr; bool is_enabled; int active_reqs; - struct workqueue_struct *ungating_workq; + struct workqueue_struct *clk_gating_workq; }; /* Hibern8 state */ diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index 37193bbb23b7..042108d4035b 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -2242,6 +2242,7 @@ static int parse_qos_dt_params(struct device_node *node, einfo->ramp_time_us[i] = arr32[i]; rc = 0; + kfree(arr32); return rc; invalid_key: diff --git a/drivers/soc/qcom/glink_spi_xprt.c b/drivers/soc/qcom/glink_spi_xprt.c index 6794c30605d7..a94c4c909a40 100644 --- a/drivers/soc/qcom/glink_spi_xprt.c +++ b/drivers/soc/qcom/glink_spi_xprt.c @@ -121,6 +121,8 @@ struct glink_cmpnt { * @tx_fifo_write_reg_addr: Address of the TX FIFO Write Index Register. * @rx_fifo_read_reg_addr: Address of the RX FIFO Read Index Register. * @rx_fifo_write_reg_addr: Address of the RX FIFO Write Index Register. + * @tx_fifo_write: Internal write index for TX FIFO. + * @rx_fifo_read: Internal read index for RX FIFO. * @kwork: Work to be executed when receiving data. * @kworker: Handle to the entity processing @kwork. * @task: Handle to the task context that runs @kworker. @@ -158,6 +160,8 @@ struct edge_info { unsigned int tx_fifo_write_reg_addr; unsigned int rx_fifo_read_reg_addr; unsigned int rx_fifo_write_reg_addr; + uint32_t tx_fifo_write; + uint32_t rx_fifo_read; struct kthread_work kwork; struct kthread_worker kworker; @@ -368,6 +372,19 @@ static int glink_spi_xprt_write_avail(struct edge_info *einfo) int write_avail; int ret; + if (unlikely(!einfo->tx_fifo_start)) { + ret = glink_spi_xprt_reg_read(einfo, + einfo->tx_fifo_write_reg_addr, &einfo->tx_fifo_write); + if (ret < 0) { + pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n", + __func__, ret, einfo->xprt_cfg.edge, + einfo->tx_fifo_write_reg_addr); + return 0; + } + einfo->tx_fifo_start = einfo->tx_fifo_write; + } + write_id = einfo->tx_fifo_write; + ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_read_reg_addr, &read_id); if (ret < 0) { @@ -377,21 +394,9 @@ static int glink_spi_xprt_write_avail(struct edge_info *einfo) return 0; } - ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_write_reg_addr, - &write_id); - if (ret < 0) { - pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n", - __func__, ret, einfo->xprt_cfg.edge, - einfo->tx_fifo_write_reg_addr); - return 0; - } - if (!read_id || !write_id) return 0; - if (unlikely(!einfo->tx_fifo_start)) - einfo->tx_fifo_start = write_id; - if (read_id > write_id) write_avail = read_id - write_id; else @@ -421,14 +426,18 @@ static int glink_spi_xprt_read_avail(struct edge_info *einfo) int read_avail; int ret; - ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_read_reg_addr, - &read_id); - if (ret < 0) { - pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n", - __func__, ret, einfo->xprt_cfg.edge, - einfo->rx_fifo_read_reg_addr); - return 0; + if (unlikely(!einfo->rx_fifo_start)) { + ret = glink_spi_xprt_reg_read(einfo, + einfo->rx_fifo_read_reg_addr, &einfo->rx_fifo_read); + if (ret < 0) { + pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n", + __func__, ret, einfo->xprt_cfg.edge, + einfo->rx_fifo_read_reg_addr); + return 0; + } + einfo->rx_fifo_start = einfo->rx_fifo_read; } + read_id = einfo->rx_fifo_read; ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_write_reg_addr, &write_id); @@ -442,9 +451,6 @@ static int glink_spi_xprt_read_avail(struct edge_info *einfo) if (!read_id || !write_id) return 0; - if (unlikely(!einfo->rx_fifo_start)) - einfo->rx_fifo_start = read_id; - if (read_id <= write_id) read_avail = write_id - read_id; else @@ -471,15 +477,7 @@ static int glink_spi_xprt_rx_cmd(struct edge_info *einfo, void *dst, uint32_t offset = 0; int ret; - ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_read_reg_addr, - &read_id); - if (ret < 0) { - pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n", - __func__, ret, einfo->xprt_cfg.edge, - einfo->rx_fifo_read_reg_addr); - return ret; - } - + read_id = einfo->rx_fifo_read; do { if ((read_id + size_to_read) >= (einfo->rx_fifo_start + einfo->fifo_size)) @@ -504,6 +502,9 @@ static int glink_spi_xprt_rx_cmd(struct edge_info *einfo, void *dst, pr_err("%s: Error %d writing %s rx_fifo_read_reg_addr %d\n", __func__, ret, einfo->xprt_cfg.edge, einfo->rx_fifo_read_reg_addr); + else + einfo->rx_fifo_read = read_id; + return ret; } @@ -526,15 +527,7 @@ static int glink_spi_xprt_tx_cmd_safe(struct edge_info *einfo, void *src, uint32_t offset = 0; int ret; - ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_write_reg_addr, - &write_id); - if (ret < 0) { - pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n", - __func__, ret, einfo->xprt_cfg.edge, - einfo->tx_fifo_write_reg_addr); - return ret; - } - + write_id = einfo->tx_fifo_write; do { if ((write_id + size_to_write) >= (einfo->tx_fifo_start + einfo->fifo_size)) @@ -559,6 +552,9 @@ static int glink_spi_xprt_tx_cmd_safe(struct edge_info *einfo, void *src, pr_err("%s: Error %d writing %s tx_fifo_write_reg_addr %d\n", __func__, ret, einfo->xprt_cfg.edge, einfo->tx_fifo_write_reg_addr); + else + einfo->tx_fifo_write = write_id; + return ret; } @@ -1236,6 +1232,8 @@ static int ssr(struct glink_transport_if *if_ptr) einfo->tx_blocked_signal_sent = false; einfo->tx_fifo_start = 0; einfo->rx_fifo_start = 0; + einfo->tx_fifo_write = 0; + einfo->rx_fifo_read = 0; einfo->fifo_size = DEFAULT_FIFO_SIZE; einfo->xprt_if.glink_core_if_ptr->link_down(&einfo->xprt_if); diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index c74cd3b814cd..2326487302fd 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -84,35 +84,59 @@ module_param(qmi_timeout, ulong, 0600); } while (0) #define icnss_pr_err(_fmt, ...) do { \ - pr_err(_fmt, ##__VA_ARGS__); \ - icnss_ipc_log_string("ERR: " pr_fmt(_fmt), \ - ##__VA_ARGS__); \ + printk("%s" pr_fmt(_fmt), KERN_ERR, ##__VA_ARGS__); \ + icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \ + ##__VA_ARGS__); \ } while (0) #define icnss_pr_warn(_fmt, ...) do { \ - pr_warn(_fmt, ##__VA_ARGS__); \ - icnss_ipc_log_string("WRN: " pr_fmt(_fmt), \ - ##__VA_ARGS__); \ + printk("%s" pr_fmt(_fmt), KERN_WARNING, ##__VA_ARGS__); \ + icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \ + ##__VA_ARGS__); \ } while (0) #define icnss_pr_info(_fmt, ...) do { \ - pr_info(_fmt, ##__VA_ARGS__); \ - icnss_ipc_log_string("INF: " pr_fmt(_fmt), \ - ##__VA_ARGS__); \ + printk("%s" pr_fmt(_fmt), KERN_INFO, ##__VA_ARGS__); \ + icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \ + ##__VA_ARGS__); \ } while (0) +#if defined(CONFIG_DYNAMIC_DEBUG) #define icnss_pr_dbg(_fmt, ...) do { \ - pr_debug(_fmt, ##__VA_ARGS__); \ - icnss_ipc_log_string("DBG: " pr_fmt(_fmt), \ - ##__VA_ARGS__); \ + pr_debug(_fmt, ##__VA_ARGS__); \ + icnss_ipc_log_string(pr_fmt(_fmt), ##__VA_ARGS__); \ } while (0) #define icnss_pr_vdbg(_fmt, ...) do { \ - pr_debug(_fmt, ##__VA_ARGS__); \ - icnss_ipc_log_long_string("DBG: " pr_fmt(_fmt), \ - ##__VA_ARGS__); \ + pr_debug(_fmt, ##__VA_ARGS__); \ + icnss_ipc_log_long_string(pr_fmt(_fmt), ##__VA_ARGS__); \ + } while (0) +#elif defined(DEBUG) +#define icnss_pr_dbg(_fmt, ...) do { \ + printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \ + icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \ + ##__VA_ARGS__); \ + } while (0) + +#define icnss_pr_vdbg(_fmt, ...) do { \ + printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \ + icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \ + ##__VA_ARGS__); \ + } while (0) +#else +#define icnss_pr_dbg(_fmt, ...) do { \ + no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \ + icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \ + ##__VA_ARGS__); \ } while (0) +#define icnss_pr_vdbg(_fmt, ...) do { \ + no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \ + icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \ + ##__VA_ARGS__); \ + } while (0) +#endif + #ifdef CONFIG_ICNSS_DEBUG #define ICNSS_ASSERT(_condition) do { \ if (!(_condition)) { \ @@ -128,6 +152,8 @@ bool ignore_qmi_timeout; #define ICNSS_QMI_ASSERT() do { } while (0) #endif +#define QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED 0x77 + enum icnss_debug_quirks { HW_ALWAYS_ON, HW_DEBUG_ENABLE, @@ -242,7 +268,6 @@ struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = { struct icnss_event_pd_service_down_data { bool crashed; bool fw_rejuvenate; - bool wdog_bite; }; struct icnss_driver_event { @@ -267,9 +292,9 @@ enum icnss_driver_state { ICNSS_PD_RESTART, ICNSS_MSA0_ASSIGNED, ICNSS_WLFW_EXISTS, - ICNSS_WDOG_BITE, ICNSS_SHUTDOWN_DONE, ICNSS_HOST_TRIGGERED_PDR, + ICNSS_FW_DOWN, }; struct ce_irq_list { @@ -480,10 +505,10 @@ static int icnss_assign_msa_perm(struct icnss_mem_region_info phys_addr_t addr; u32 size; u32 i = 0; - u32 source_vmids[ICNSS_MAX_VMIDS]; + u32 source_vmids[ICNSS_MAX_VMIDS] = {0}; u32 source_nelems; - u32 dest_vmids[ICNSS_MAX_VMIDS]; - u32 dest_perms[ICNSS_MAX_VMIDS]; + u32 dest_vmids[ICNSS_MAX_VMIDS] = {0}; + u32 dest_perms[ICNSS_MAX_VMIDS] = {0}; u32 dest_nelems; enum icnss_msa_perm cur_perm = mem_region->perm; struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list; @@ -711,7 +736,7 @@ static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv, if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n", resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } priv->stats.vbatt_resp++; @@ -1195,7 +1220,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void) if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n", resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } @@ -1267,7 +1292,7 @@ static int wlfw_msa_ready_send_sync_msg(void) if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n", resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } penv->stats.msa_ready_resp++; @@ -1330,7 +1355,7 @@ static int wlfw_ind_register_send_sync_msg(void) if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n", resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } penv->stats.ind_register_resp++; @@ -1377,7 +1402,9 @@ static int wlfw_cap_send_sync_msg(void) if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI capability request rejected, result:%d error:%d\n", resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; + if (resp.resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED) + icnss_pr_err("RF card Not present"); goto out; } @@ -1460,7 +1487,7 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode) if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n", mode, resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } penv->stats.mode_resp++; @@ -1510,7 +1537,7 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data) if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI config request rejected, result:%d error:%d\n", resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } penv->stats.cfg_resp++; @@ -1563,7 +1590,7 @@ static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode) if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n", fw_log_mode, resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } penv->stats.ini_resp++; @@ -1623,7 +1650,7 @@ static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv, if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n", resp->resp.result, resp->resp.error); - ret = resp->resp.result; + ret = -resp->resp.result; goto out; } @@ -1689,7 +1716,7 @@ static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv, if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n", resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } out: @@ -1784,7 +1811,7 @@ static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv) if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n", resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } priv->stats.rejuvenate_ack_resp++; @@ -1845,7 +1872,7 @@ static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv, if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n", resp.resp.result, resp.resp.error); - ret = resp.resp.result; + ret = -resp.resp.result; goto out; } @@ -1924,6 +1951,12 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle, icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len); + if (test_bit(ICNSS_FW_DOWN, &penv->state)) { + icnss_pr_dbg("FW down, ignoring 0x%x, state: 0x%lx\n", + msg_id, penv->state); + return; + } + switch (msg_id) { case QMI_WLFW_FW_READY_IND_V01: icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND, @@ -1970,6 +2003,7 @@ static int icnss_driver_event_server_arrive(void *data) return -ENODEV; set_bit(ICNSS_WLFW_EXISTS, &penv->state); + clear_bit(ICNSS_FW_DOWN, &penv->state); penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv); if (!penv->wlfw_clnt) { @@ -2125,10 +2159,7 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv) icnss_pm_relax(priv); - if (test_bit(ICNSS_WDOG_BITE, &priv->state)) { - icnss_call_driver_shutdown(priv); - clear_bit(ICNSS_WDOG_BITE, &priv->state); - } + icnss_call_driver_shutdown(priv); clear_bit(ICNSS_PD_RESTART, &priv->state); @@ -2278,8 +2309,7 @@ static int icnss_call_driver_remove(struct icnss_priv *priv) static int icnss_fw_crashed(struct icnss_priv *priv, struct icnss_event_pd_service_down_data *event_data) { - icnss_pr_dbg("FW crashed, state: 0x%lx, wdog_bite: %d\n", - priv->state, event_data->wdog_bite); + icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state); set_bit(ICNSS_PD_RESTART, &priv->state); clear_bit(ICNSS_FW_READY, &priv->state); @@ -2289,17 +2319,9 @@ static int icnss_fw_crashed(struct icnss_priv *priv, if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL); - if (event_data->wdog_bite) { - set_bit(ICNSS_WDOG_BITE, &priv->state); - goto out; - } - - icnss_call_driver_shutdown(priv); - if (event_data->fw_rejuvenate) wlfw_rejuvenate_ack_send_sync_msg(priv); -out: return 0; } @@ -2482,6 +2504,8 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n", priv->state, notif->crashed); + set_bit(ICNSS_FW_DOWN, &priv->state); + if (notif->crashed) priv->stats.recovery.root_pd_crash++; else @@ -2496,9 +2520,6 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, event_data->crashed = notif->crashed; - if (notif->crashed == CRASH_STATUS_WDOG_BITE) - event_data->wdog_bite = true; - fw_down_data.crashed = !!notif->crashed; icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data); @@ -2579,21 +2600,21 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, if (event_data == NULL) return notifier_from_errno(-ENOMEM); + event_data->crashed = true; + if (state == NULL) { - event_data->crashed = true; priv->stats.recovery.root_pd_crash++; goto event_post; } switch (*state) { case ROOT_PD_WDOG_BITE: - event_data->crashed = true; - event_data->wdog_bite = true; priv->stats.recovery.root_pd_crash++; break; case ROOT_PD_SHUTDOWN: cause = ICNSS_ROOT_PD_SHUTDOWN; priv->stats.recovery.root_pd_shutdown++; + event_data->crashed = false; break; case USER_PD_STATE_CHANGE: if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) { @@ -2605,7 +2626,6 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, } break; default: - event_data->crashed = true; priv->stats.recovery.root_pd_crash++; break; } @@ -2613,6 +2633,7 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n", *state, priv->state, icnss_pdr_cause[cause]); event_post: + set_bit(ICNSS_FW_DOWN, &priv->state); icnss_ignore_qmi_timeout(true); clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state); @@ -2621,6 +2642,8 @@ event_post: icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, ICNSS_EVENT_SYNC, event_data); done: + if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01) + clear_bit(ICNSS_FW_DOWN, &priv->state); return NOTIFY_OK; } @@ -3808,15 +3831,15 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_WLFW_EXISTS: seq_puts(s, "WLAN FW EXISTS"); continue; - case ICNSS_WDOG_BITE: - seq_puts(s, "MODEM WDOG BITE"); - continue; case ICNSS_SHUTDOWN_DONE: seq_puts(s, "SHUTDOWN DONE"); continue; case ICNSS_HOST_TRIGGERED_PDR: seq_puts(s, "HOST TRIGGERED PDR"); continue; + case ICNSS_FW_DOWN: + seq_puts(s, "FW DOWN"); + continue; } seq_printf(s, "UNKNOWN-%d", i); diff --git a/drivers/soc/qcom/jtagv8-etm.c b/drivers/soc/qcom/jtagv8-etm.c index 2c15f7896c82..63432e6026e2 100644 --- a/drivers/soc/qcom/jtagv8-etm.c +++ b/drivers/soc/qcom/jtagv8-etm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1503,6 +1503,7 @@ static int jtag_mm_etm_callback(struct notifier_block *nfb, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; + u64 version = 0; if (!etm[cpu]) goto out; @@ -1524,8 +1525,8 @@ static int jtag_mm_etm_callback(struct notifier_block *nfb, goto out; } if (etm_arch_supported(etm[cpu]->arch)) { - if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < - TZ_DBG_ETM_VER) + if (!scm_get_feat_version(TZ_DBG_ETM_FEAT_ID, &version) + && version < TZ_DBG_ETM_VER) etm[cpu]->save_restore_enabled = true; else pr_info("etm save-restore supported by TZ\n"); @@ -1615,8 +1616,10 @@ static int jtag_mm_etm_probe(struct platform_device *pdev, uint32_t cpu) mutex_lock(&etmdata->mutex); if (etmdata->init && !etmdata->enable) { if (etm_arch_supported(etmdata->arch)) { - if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < - TZ_DBG_ETM_VER) + u64 version = 0; + + if (!scm_get_feat_version(TZ_DBG_ETM_FEAT_ID, &version) + && (version < TZ_DBG_ETM_VER)) etmdata->save_restore_enabled = true; else pr_info("etm save-restore supported by TZ\n"); diff --git a/drivers/soc/qcom/jtagv8.c b/drivers/soc/qcom/jtagv8.c index 94c391eabaea..f09ccce8f9c3 100644 --- a/drivers/soc/qcom/jtagv8.c +++ b/drivers/soc/qcom/jtagv8.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -974,7 +974,7 @@ static struct notifier_block jtag_cpu_pm_notifier = { static int __init msm_jtag_dbg_init(void) { int ret; - + u64 version = 0; if (msm_jtag_fuse_apps_access_disabled()) return -EPERM; @@ -982,7 +982,8 @@ static int __init msm_jtag_dbg_init(void) dbg_init_arch_data(); if (dbg_arch_supported(dbg.arch)) { - if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < TZ_DBG_ETM_VER) { + if (!scm_get_feat_version(TZ_DBG_ETM_FEAT_ID, &version) && + version < TZ_DBG_ETM_VER) { dbg.save_restore_enabled = true; } else { pr_info("dbg save-restore supported by TZ\n"); diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index ace2bc4f30b6..d77a12626330 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -56,7 +56,9 @@ #endif #define PIL_NUM_DESC 10 +#define NUM_OF_ENCRYPTED_KEY 3 static void __iomem *pil_info_base; +static void __iomem *pil_minidump_base; /** * proxy_timeout - Override for proxy vote timeouts @@ -80,6 +82,18 @@ struct pil_mdt { }; /** + * struct boot_minidump_smem_region - Representation of SMEM TOC + * @region_name: Name of modem segment to be dumped + * @region_base_address: Where segment start from + * @region_size: Size of segment to be dumped + */ +struct boot_minidump_smem_region { + char region_name[16]; + u64 region_base_address; + u64 region_size; +}; + +/** * struct pil_seg - memory map representing one segment * @next: points to next seg mentor NULL if last segment * @paddr: physical start address of segment @@ -133,11 +147,67 @@ struct pil_priv { phys_addr_t region_end; void *region; struct pil_image_info __iomem *info; + struct md_ssr_ss_info __iomem *minidump; + int minidump_id; int id; int unvoted_flag; size_t region_size; }; +static int pil_do_minidump(struct pil_desc *desc, void *ramdump_dev) +{ + struct boot_minidump_smem_region __iomem *region_info; + struct ramdump_segment *ramdump_segs, *s; + struct pil_priv *priv = desc->priv; + void __iomem *subsys_smem_base; + void __iomem *offset; + int ss_mdump_seg_cnt; + int ret, i; + + memcpy(&offset, &priv->minidump, sizeof(priv->minidump)); + offset = offset + sizeof(priv->minidump->md_ss_smem_regions_baseptr); + /* There are 3 encryption keys which also need to be dumped */ + ss_mdump_seg_cnt = readb_relaxed(offset) + + NUM_OF_ENCRYPTED_KEY; + + subsys_smem_base = ioremap(__raw_readl(priv->minidump), + ss_mdump_seg_cnt * sizeof(*region_info)); + region_info = + (struct boot_minidump_smem_region __iomem *)subsys_smem_base; + ramdump_segs = kcalloc(ss_mdump_seg_cnt, + sizeof(*ramdump_segs), GFP_KERNEL); + if (!ramdump_segs) + return -ENOMEM; + + if (desc->subsys_vmid > 0) + ret = pil_assign_mem_to_linux(desc, priv->region_start, + (priv->region_end - priv->region_start)); + + s = ramdump_segs; + for (i = 0; i < ss_mdump_seg_cnt; i++) { + memcpy(&offset, ®ion_info, sizeof(region_info)); + memcpy(&s->name, ®ion_info, sizeof(region_info)); + offset = offset + sizeof(region_info->region_name); + s->address = __raw_readl(offset); + offset = offset + sizeof(region_info->region_base_address); + s->size = __raw_readl(offset); + s++; + region_info++; + } + ret = do_minidump(ramdump_dev, ramdump_segs, ss_mdump_seg_cnt); + kfree(ramdump_segs); + if (ret) + pil_err(desc, "%s: Ramdump collection failed for subsys %s rc:%d\n", + __func__, desc->name, ret); + writel_relaxed(0, &priv->minidump->md_ss_smem_regions_baseptr); + writeb_relaxed(1, &priv->minidump->md_ss_ssr_cause); + + if (desc->subsys_vmid > 0) + ret = pil_assign_mem_to_subsys(desc, priv->region_start, + (priv->region_end - priv->region_start)); + return ret; +} + /** * pil_do_ramdump() - Ramdump an image * @desc: descriptor from pil_desc_init() @@ -153,6 +223,9 @@ int pil_do_ramdump(struct pil_desc *desc, void *ramdump_dev) int count = 0, ret; struct ramdump_segment *ramdump_segs, *s; + if (priv->minidump && (__raw_readl(priv->minidump) > 0)) + return pil_do_minidump(desc, ramdump_dev); + list_for_each_entry(seg, &priv->segs, list) count++; @@ -1014,9 +1087,10 @@ bool is_timeout_disabled(void) int pil_desc_init(struct pil_desc *desc) { struct pil_priv *priv; - int ret; void __iomem *addr; + int ret, ss_imem_offset_mdump; char buf[sizeof(priv->info->name)]; + struct device_node *ofnode = desc->dev->of_node; if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote, "Invalid proxy voting. Ignoring\n")) @@ -1039,6 +1113,22 @@ int pil_desc_init(struct pil_desc *desc) strncpy(buf, desc->name, sizeof(buf)); __iowrite32_copy(priv->info->name, buf, sizeof(buf) / 4); } + if (of_property_read_u32(ofnode, "qcom,minidump-id", + &priv->minidump_id)) + pr_debug("minidump-id not found for %s\n", desc->name); + else { + ss_imem_offset_mdump = + sizeof(struct md_ssr_ss_info) * priv->minidump_id; + if (pil_minidump_base) { + /* Add 0x4 to get start of struct md_ssr_ss_info base + * from struct md_ssr_toc for any subsystem, + * struct md_ssr_ss_info is actually the pointer + * of ToC in smem for any subsystem. + */ + addr = pil_minidump_base + ss_imem_offset_mdump + 0x4; + priv->minidump = (struct md_ssr_ss_info __iomem *)addr; + } + } ret = pil_parse_devicetree(desc); if (ret) @@ -1148,6 +1238,20 @@ static int __init msm_pil_init(void) for (i = 0; i < resource_size(&res)/sizeof(u32); i++) writel_relaxed(0, pil_info_base + (i * sizeof(u32))); + np = of_find_compatible_node(NULL, NULL, "qcom,msm-imem-minidump"); + if (!np) { + pr_warn("pil: failed to find qcom,msm-imem-minidump node\n"); + goto out; + } else { + pil_minidump_base = of_iomap(np, 0); + if (!pil_minidump_base) { + pr_err("unable to map pil minidump imem offset\n"); + goto out; + } + } + for (i = 0; i < sizeof(struct md_ssr_toc)/sizeof(u32); i++) + writel_relaxed(0, pil_minidump_base + (i * sizeof(u32))); + writel_relaxed(1, pil_minidump_base); out: return register_pm_notifier(&pil_pm_notifier); } @@ -1158,6 +1262,8 @@ static void __exit msm_pil_exit(void) unregister_pm_notifier(&pil_pm_notifier); if (pil_info_base) iounmap(pil_info_base); + if (pil_minidump_base) + iounmap(pil_minidump_base); } module_exit(msm_pil_exit); diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h index 0cd2aeae1edd..908ab78124f7 100644 --- a/drivers/soc/qcom/peripheral-loader.h +++ b/drivers/soc/qcom/peripheral-loader.h @@ -74,6 +74,34 @@ struct pil_image_info { __le32 size; } __attribute__((__packed__)); +#define MAX_NUM_OF_SS 3 + +/** + * struct md_ssr_ss_info - Info in imem about smem ToC + * @md_ss_smem_regions_baseptr: Start physical address of SMEM TOC + * @md_ss_num_of_regions: number of segments that need to be dumped + * @md_ss_encryption_status: status of encryption of segments + * @md_ss_ssr_cause: ssr cause enum + */ +struct md_ssr_ss_info { + u32 md_ss_smem_regions_baseptr; + u8 md_ss_num_of_regions; + u8 md_ss_encryption_status; + u8 md_ss_ssr_cause; + u8 reserved; +}; + +/** + * struct md_ssr_toc - Wrapper of struct md_ssr_ss_info + * @md_ssr_toc_init: flag to indicate to MSS SW about imem init done + * @md_ssr_ss: Instance of struct md_ssr_ss_info for a subsystem + */ +struct md_ssr_toc /* Shared IMEM ToC struct */ +{ + u32 md_ssr_toc_init; + struct md_ssr_ss_info md_ssr_ss[MAX_NUM_OF_SS]; +}; + /** * struct pil_reset_ops - PIL operations * @init_image: prepare an image for authentication diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c index 5fcb0f95733c..4bea034f0bdd 100644 --- a/drivers/soc/qcom/pil-msa.c +++ b/drivers/soc/qcom/pil-msa.c @@ -78,7 +78,8 @@ #define MSS_MAGIC 0XAABADEAD /* CX_IPEAK Parameters */ #define CX_IPEAK_MSS BIT(5) - +/* Timeout value for MBA boot when minidump is enabled */ +#define MBA_ENCRYPTION_TIMEOUT 3000 enum scm_cmd { PAS_MEM_SETUP_CMD = 2, }; @@ -244,7 +245,12 @@ static int pil_msa_wait_for_mba_ready(struct q6v5_data *drv) struct device *dev = drv->desc.dev; int ret; u32 status; - u64 val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000; + u64 val; + + if (of_property_read_bool(dev->of_node, "qcom,minidump-id")) + pbl_mba_boot_timeout_ms = MBA_ENCRYPTION_TIMEOUT; + + val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000; /* Wait for PBL completion. */ ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status, diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c index d14e82415c5a..7a62c2e36da8 100644 --- a/drivers/soc/qcom/qbt1000.c +++ b/drivers/soc/qcom/qbt1000.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) "qbt1000:%s: " fmt, __func__ +#include <linux/delay.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> @@ -105,10 +106,14 @@ struct qbt1000_drvdata { */ struct fw_ipc_cmd { uint32_t status; + uint32_t numMsgs; + uint8_t msg_data[FW_MAX_IPC_MSG_DATA_SIZE]; +}; + +struct fw_ipc_header { uint32_t msg_type; uint32_t msg_len; uint32_t resp_needed; - uint8_t msg_data[FW_MAX_IPC_MSG_DATA_SIZE]; }; /* @@ -352,7 +357,14 @@ static int qbt1000_open(struct inode *inode, struct file *file) */ static int qbt1000_release(struct inode *inode, struct file *file) { - struct qbt1000_drvdata *drvdata = file->private_data; + struct qbt1000_drvdata *drvdata; + + if (!file->private_data) { + pr_err("Null pointer passed in file->private_data"); + return -EINVAL; + } + + drvdata = file->private_data; atomic_inc(&drvdata->available); return 0; @@ -374,6 +386,11 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) void __user *priv_arg = (void __user *)arg; struct qbt1000_drvdata *drvdata; + if (!file->private_data) { + pr_err("Null pointer passed in file->private_data"); + return -EINVAL; + } + drvdata = file->private_data; if (IS_ERR(priv_arg)) { @@ -789,6 +806,8 @@ static int qbt1000_create_input_device(struct qbt1000_drvdata *drvdata) BIT_MASK(KEY_HOMEPAGE); drvdata->in_dev->keybit[BIT_WORD(KEY_CAMERA)] |= BIT_MASK(KEY_CAMERA); + drvdata->in_dev->keybit[BIT_WORD(KEY_VOLUMEDOWN)] |= + BIT_MASK(KEY_VOLUMEDOWN); drvdata->in_dev->keybit[BIT_WORD(KEY_POWER)] |= BIT_MASK(KEY_POWER); @@ -917,11 +936,14 @@ static irqreturn_t qbt1000_gpio_isr(int irq, void *dev_id) */ static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id) { + uint8_t *msg_buffer; struct fw_ipc_cmd *rx_cmd; - int i; + struct fw_ipc_header *header; + int i, j; uint32_t rxipc = FP_APP_CMD_RX_IPC; struct qbt1000_drvdata *drvdata = (struct qbt1000_drvdata *)dev_id; int rc = 0; + uint32_t retry_count = 10; pm_stay_awake(drvdata->dev); @@ -933,18 +955,25 @@ static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id) goto end; } - pr_debug("firmware interrupt received (irq %d)\n", irq); - if (!drvdata->fp_app_handle) goto end; - /* - * send the TZ command to fetch the message from firmware - * TZ will process the message if it can - */ - rc = send_tz_cmd(drvdata, drvdata->fp_app_handle, 0, - &rxipc, sizeof(rxipc), - (void *)&rx_cmd, sizeof(*rx_cmd)); + while (retry_count > 0) { + /* + * send the TZ command to fetch the message from firmware + * TZ will process the message if it can + */ + rc = send_tz_cmd(drvdata, drvdata->fp_app_handle, 0, + &rxipc, sizeof(rxipc), + (void *)&rx_cmd, sizeof(*rx_cmd)); + if (rc < 0) { + msleep(50); /* sleep for 50ms before retry */ + retry_count -= 1; + continue; + } else { + break; + } + } if (rc < 0) { pr_err("failure sending tz cmd %d\n", rxipc); @@ -956,29 +985,35 @@ static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id) goto end; } - /* - * given the IPC message type, search for a corresponding event for the - * driver client. If found, add to the events FIFO - */ - for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) { - if (g_msg_to_event[i].msg_type == rx_cmd->msg_type) { - enum qbt1000_fw_event ev = g_msg_to_event[i].fw_event; - struct fw_event_desc fw_ev_desc; - - mutex_lock(&drvdata->fw_events_mutex); - pr_debug("fw events: add %d\n", (int) ev); - fw_ev_desc.ev = ev; - - if (!kfifo_put(&drvdata->fw_events, fw_ev_desc)) - pr_err("fw events: fifo full, drop event %d\n", - (int) ev); - - mutex_unlock(&drvdata->fw_events_mutex); - wake_up_interruptible(&drvdata->read_wait_queue); - break; + msg_buffer = rx_cmd->msg_data; + + for (j = 0; j < rx_cmd->numMsgs; j++) { + header = (struct fw_ipc_header *) msg_buffer; + /* + * given the IPC message type, search for a corresponding event + * for the driver client. If found, add to the events FIFO + */ + for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) { + if (g_msg_to_event[i].msg_type == header->msg_type) { + enum qbt1000_fw_event ev = + g_msg_to_event[i].fw_event; + struct fw_event_desc fw_ev_desc; + + mutex_lock(&drvdata->fw_events_mutex); + pr_debug("fw events: add %d\n", (int) ev); + fw_ev_desc.ev = ev; + + if (!kfifo_put(&drvdata->fw_events, fw_ev_desc)) + pr_err("fw events: fifo full, drop event %d\n", + (int) ev); + + mutex_unlock(&drvdata->fw_events_mutex); + break; + } } + msg_buffer += sizeof(*header) + header->msg_len; } - + wake_up_interruptible(&drvdata->read_wait_queue); end: mutex_unlock(&drvdata->mutex); pm_relax(drvdata->dev); diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c index eca992ec17e4..3791169ec0ac 100644 --- a/drivers/soc/qcom/qdsp6v2/apr.c +++ b/drivers/soc/qcom/qdsp6v2/apr.c @@ -820,6 +820,7 @@ static void dispatch_event(unsigned long code, uint16_t proc) uint16_t clnt; int i, j; + memset(&data, 0, sizeof(data)); data.opcode = RESET_EVENTS; data.reset_event = code; diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c index 83e3775ed533..9e61ff1ebfcc 100644 --- a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c +++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,7 @@ #include <linux/export.h> #include <linux/qcom_iommu.h> #include <asm/dma-iommu.h> +#include <soc/qcom/secure_buffer.h> #define MSM_AUDIO_ION_PROBED (1 << 0) @@ -178,6 +179,123 @@ err: } EXPORT_SYMBOL(msm_audio_ion_alloc); +static int msm_audio_hyp_assign(ion_phys_addr_t *paddr, size_t *pa_len, + u8 assign_type) +{ + int srcVM[1] = {VMID_HLOS}; + int destVM[1] = {VMID_CP_ADSP_SHARED}; + int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC}; + int ret = 0; + + switch (assign_type) { + case HLOS_TO_ADSP: + srcVM[0] = VMID_HLOS; + destVM[0] = VMID_CP_ADSP_SHARED; + break; + case ADSP_TO_HLOS: + srcVM[0] = VMID_CP_ADSP_SHARED; + destVM[0] = VMID_HLOS; + break; + default: + pr_err("%s: Invalid assign type = %d\n", __func__, assign_type); + ret = -EINVAL; + goto done; + } + + ret = hyp_assign_phys(*paddr, *pa_len, srcVM, 1, destVM, destVMperm, 1); + if (ret) + pr_err("%s: hyp_assign_phys failed for type %d, rc = %d\n", + __func__, assign_type, ret); +done: + return ret; +} + +int msm_audio_ion_phys_free(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *paddr, + size_t *pa_len, u8 assign_type) +{ + int ret; + + if (!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) { + pr_debug("%s:probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + + if (!client || !handle || !paddr || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + + ret = ion_phys(client, handle, paddr, pa_len); + if (ret) { + pr_err("%s: could not get physical address for handle, ret = %d\n", + __func__, ret); + goto err_ion_handle; + } + + ret = msm_audio_hyp_assign(paddr, pa_len, assign_type); + +err_ion_handle: + ion_free(client, handle); + ion_client_destroy(client); + + return ret; +} + +int msm_audio_ion_phys_assign(const char *name, struct ion_client **client, + struct ion_handle **handle, int fd, + ion_phys_addr_t *paddr, + size_t *pa_len, u8 assign_type) +{ + int ret; + + if (!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) { + pr_debug("%s:probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + + if (!name || !client || !handle || !paddr || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + + *client = msm_audio_ion_client_create(name); + if (IS_ERR_OR_NULL((void *)(*client))) { + pr_err("%s: ION create client failed\n", __func__); + return -EINVAL; + } + + *handle = ion_import_dma_buf(*client, fd); + if (IS_ERR_OR_NULL((void *) (*handle))) { + pr_err("%s: ion import dma buffer failed\n", + __func__); + ret = -EINVAL; + goto err_destroy_client; + } + + ret = ion_phys(*client, *handle, paddr, pa_len); + if (ret) { + pr_err("%s: could not get physical address for handle, ret = %d\n", + __func__, ret); + goto err_ion_handle; + } + + ret = msm_audio_hyp_assign(paddr, pa_len, assign_type); + + return ret; + +err_ion_handle: + ion_free(*client, *handle); + +err_destroy_client: + ion_client_destroy(*client); + *client = NULL; + *handle = NULL; + + return ret; +} + int msm_audio_ion_import(const char *name, struct ion_client **client, struct ion_handle **handle, int fd, unsigned long *ionflag, size_t bufsz, diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c index d86f8671705a..38cc86963181 100644 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -2233,13 +2233,23 @@ static void qpnp_hap_td_enable(struct timed_output_dev *dev, int time_ms) timed_dev); int rc; - if (time_ms <= 0) + if (time_ms < 0) return; + mutex_lock(&hap->lock); + + if (time_ms == 0) { + /* disable haptics */ + hrtimer_cancel(&hap->hap_timer); + hap->state = 0; + schedule_work(&hap->work); + mutex_unlock(&hap->lock); + return; + } + if (time_ms < 10) time_ms = 10; - mutex_lock(&hap->lock); if (is_sw_lra_auto_resonance_control(hap)) hrtimer_cancel(&hap->auto_res_err_poll_timer); diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c index c712ed392b0b..c8353dc8a43a 100644 --- a/drivers/soc/qcom/ramdump.c +++ b/drivers/soc/qcom/ramdump.c @@ -29,6 +29,8 @@ #include <linux/of.h> #define RAMDUMP_WAIT_MSECS 120000 +#define MAX_STRTBL_SIZE 512 +#define MAX_NAME_LENGTH 16 struct ramdump_device { char name[256]; @@ -391,12 +393,143 @@ static int _do_ramdump(void *handle, struct ramdump_segment *segments, } +static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr) +{ + return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff); +} + +static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx) +{ + return &elf_sheader(hdr)[idx]; +} + +static inline char *elf_str_table(struct elfhdr *hdr) +{ + if (hdr->e_shstrndx == SHN_UNDEF) + return NULL; + return (char *)hdr + elf_section(hdr, hdr->e_shstrndx)->sh_offset; +} + +static inline unsigned int set_section_name(const char *name, + struct elfhdr *ehdr) +{ + char *strtab = elf_str_table(ehdr); + static int strtable_idx = 1; + int idx, ret = 0; + + idx = strtable_idx; + if ((strtab == NULL) || (name == NULL)) + return 0; + + ret = idx; + idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH); + strtable_idx = idx + 1; + + return ret; +} + +static int _do_minidump(void *handle, struct ramdump_segment *segments, + int nsegments) +{ + int ret, i; + struct ramdump_device *rd_dev = (struct ramdump_device *)handle; + struct elfhdr *ehdr; + struct elf_shdr *shdr; + unsigned long offset, strtbl_off; + + if (!rd_dev->consumer_present) { + pr_err("Ramdump(%s): No consumers. Aborting..\n", rd_dev->name); + return -EPIPE; + } + + rd_dev->segments = segments; + rd_dev->nsegments = nsegments; + + rd_dev->elfcore_size = sizeof(*ehdr) + + (sizeof(*shdr) * (nsegments + 2)) + MAX_STRTBL_SIZE; + ehdr = kzalloc(rd_dev->elfcore_size, GFP_KERNEL); + rd_dev->elfcore_buf = (char *)ehdr; + if (!rd_dev->elfcore_buf) + return -ENOMEM; + + memcpy(ehdr->e_ident, ELFMAG, SELFMAG); + ehdr->e_ident[EI_CLASS] = ELF_CLASS; + ehdr->e_ident[EI_DATA] = ELF_DATA; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELF_OSABI; + ehdr->e_type = ET_CORE; + ehdr->e_machine = ELF_ARCH; + ehdr->e_version = EV_CURRENT; + ehdr->e_ehsize = sizeof(*ehdr); + ehdr->e_shoff = sizeof(*ehdr); + ehdr->e_shentsize = sizeof(*shdr); + ehdr->e_shstrndx = 1; + + + offset = rd_dev->elfcore_size; + shdr = (struct elf_shdr *)(ehdr + 1); + strtbl_off = sizeof(*ehdr) + sizeof(*shdr) * (nsegments + 2); + shdr++; + shdr->sh_type = SHT_STRTAB; + shdr->sh_offset = (elf_addr_t)strtbl_off; + shdr->sh_size = MAX_STRTBL_SIZE; + shdr->sh_entsize = 0; + shdr->sh_flags = 0; + shdr->sh_name = set_section_name("STR_TBL", ehdr); + shdr++; + + for (i = 0; i < nsegments; i++, shdr++) { + /* Update elf header */ + shdr->sh_type = SHT_PROGBITS; + shdr->sh_name = set_section_name(segments[i].name, ehdr); + shdr->sh_addr = (elf_addr_t)segments[i].address; + shdr->sh_size = segments[i].size; + shdr->sh_flags = SHF_WRITE; + shdr->sh_offset = offset; + shdr->sh_entsize = 0; + offset += shdr->sh_size; + } + ehdr->e_shnum = nsegments + 2; + + rd_dev->data_ready = 1; + rd_dev->ramdump_status = -1; + + reinit_completion(&rd_dev->ramdump_complete); + + /* Tell userspace that the data is ready */ + wake_up(&rd_dev->dump_wait_q); + + /* Wait (with a timeout) to let the ramdump complete */ + ret = wait_for_completion_timeout(&rd_dev->ramdump_complete, + msecs_to_jiffies(RAMDUMP_WAIT_MSECS)); + + if (!ret) { + pr_err("Ramdump(%s): Timed out waiting for userspace.\n", + rd_dev->name); + ret = -EPIPE; + } else { + ret = (rd_dev->ramdump_status == 0) ? 0 : -EPIPE; + } + + rd_dev->data_ready = 0; + rd_dev->elfcore_size = 0; + kfree(rd_dev->elfcore_buf); + rd_dev->elfcore_buf = NULL; + return ret; +} + int do_ramdump(void *handle, struct ramdump_segment *segments, int nsegments) { return _do_ramdump(handle, segments, nsegments, false); } EXPORT_SYMBOL(do_ramdump); +int do_minidump(void *handle, struct ramdump_segment *segments, int nsegments) +{ + return _do_minidump(handle, segments, nsegments); +} +EXPORT_SYMBOL(do_minidump); + int do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments) { diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c index 31de6e5c173c..43e2e4d17648 100644 --- a/drivers/soc/qcom/scm.c +++ b/drivers/soc/qcom/scm.c @@ -397,18 +397,22 @@ static int __scm_call_armv8_64(u64 x0, u64 x1, u64 x2, u64 x3, u64 x4, u64 x5, __asmeq("%1", R1_STR) __asmeq("%2", R2_STR) __asmeq("%3", R3_STR) - __asmeq("%4", R0_STR) - __asmeq("%5", R1_STR) - __asmeq("%6", R2_STR) - __asmeq("%7", R3_STR) - __asmeq("%8", R4_STR) - __asmeq("%9", R5_STR) - __asmeq("%10", R6_STR) + __asmeq("%4", R4_STR) + __asmeq("%5", R5_STR) + __asmeq("%6", R6_STR) + __asmeq("%7", R0_STR) + __asmeq("%8", R1_STR) + __asmeq("%9", R2_STR) + __asmeq("%10", R3_STR) + __asmeq("%11", R4_STR) + __asmeq("%12", R5_STR) + __asmeq("%13", R6_STR) #ifdef REQUIRES_SEC ".arch_extension sec\n" #endif "smc #0\n" - : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) + : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3), + "=r" (r4), "=r" (r5), "=r" (r6) : "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4), "r" (r5), "r" (r6) : "x7", "x8", "x9", "x10", "x11", "x12", "x13", @@ -442,18 +446,22 @@ static int __scm_call_armv8_32(u32 w0, u32 w1, u32 w2, u32 w3, u32 w4, u32 w5, __asmeq("%1", R1_STR) __asmeq("%2", R2_STR) __asmeq("%3", R3_STR) - __asmeq("%4", R0_STR) - __asmeq("%5", R1_STR) - __asmeq("%6", R2_STR) - __asmeq("%7", R3_STR) - __asmeq("%8", R4_STR) - __asmeq("%9", R5_STR) - __asmeq("%10", R6_STR) + __asmeq("%4", R4_STR) + __asmeq("%5", R5_STR) + __asmeq("%6", R6_STR) + __asmeq("%7", R0_STR) + __asmeq("%8", R1_STR) + __asmeq("%9", R2_STR) + __asmeq("%10", R3_STR) + __asmeq("%11", R4_STR) + __asmeq("%12", R5_STR) + __asmeq("%13", R6_STR) #ifdef REQUIRES_SEC ".arch_extension sec\n" #endif "smc #0\n" - : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) + : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3), + "=r" (r4), "=r" (r5), "=r" (r6) : "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4), "r" (r5), "r" (r6) : "x7", "x8", "x9", "x10", "x11", "x12", "x13", @@ -490,18 +498,22 @@ static int __scm_call_armv8_32(u32 w0, u32 w1, u32 w2, u32 w3, u32 w4, u32 w5, __asmeq("%1", R1_STR) __asmeq("%2", R2_STR) __asmeq("%3", R3_STR) - __asmeq("%4", R0_STR) - __asmeq("%5", R1_STR) - __asmeq("%6", R2_STR) - __asmeq("%7", R3_STR) - __asmeq("%8", R4_STR) - __asmeq("%9", R5_STR) - __asmeq("%10", R6_STR) + __asmeq("%4", R4_STR) + __asmeq("%5", R5_STR) + __asmeq("%6", R6_STR) + __asmeq("%7", R0_STR) + __asmeq("%8", R1_STR) + __asmeq("%9", R2_STR) + __asmeq("%10", R3_STR) + __asmeq("%11", R4_STR) + __asmeq("%12", R5_STR) + __asmeq("%13", R6_STR) #ifdef REQUIRES_SEC ".arch_extension sec\n" #endif "smc #0\n" - : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) + : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3), + "=r" (r4), "=r" (r5), "=r" (r6) : "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4), "r" (r5), "r" (r6)); @@ -1117,54 +1129,55 @@ int scm_is_call_available(u32 svc_id, u32 cmd_id) ret = scm_call(SCM_SVC_INFO, IS_CALL_AVAIL_CMD, &svc_cmd, sizeof(svc_cmd), &ret_val, sizeof(ret_val)); - if (ret) - return ret; + if (!ret && ret_val) + return 1; + else + return 0; - return ret_val; } desc.arginfo = SCM_ARGS(1); desc.args[0] = SCM_SIP_FNID(svc_id, cmd_id); + desc.ret[0] = 0; ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, IS_CALL_AVAIL_CMD), &desc); - if (ret) - return ret; + if (!ret && desc.ret[0]) + return 1; + else + return 0; - return desc.ret[0]; } EXPORT_SYMBOL(scm_is_call_available); #define GET_FEAT_VERSION_CMD 3 -int scm_get_feat_version(u32 feat) +int scm_get_feat_version(u32 feat, u64 *scm_ret) { struct scm_desc desc = {0}; int ret; if (!is_scm_armv8()) { if (scm_is_call_available(SCM_SVC_INFO, GET_FEAT_VERSION_CMD)) { - u32 version; - if (!scm_call(SCM_SVC_INFO, GET_FEAT_VERSION_CMD, &feat, - sizeof(feat), &version, sizeof(version))) - return version; + ret = scm_call(SCM_SVC_INFO, GET_FEAT_VERSION_CMD, + &feat, sizeof(feat), scm_ret, sizeof(*scm_ret)); + return ret; } - return 0; } ret = scm_is_call_available(SCM_SVC_INFO, GET_FEAT_VERSION_CMD); if (ret <= 0) - return 0; + return -EAGAIN; desc.args[0] = feat; desc.arginfo = SCM_ARGS(1); ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, GET_FEAT_VERSION_CMD), &desc); - if (!ret) - return desc.ret[0]; - return 0; + *scm_ret = desc.ret[0]; + + return ret; } EXPORT_SYMBOL(scm_get_feat_version); #define RESTORE_SEC_CFG 2 -int scm_restore_sec_cfg(u32 device_id, u32 spare, int *scm_ret) +int scm_restore_sec_cfg(u32 device_id, u32 spare, u64 *scm_ret) { struct scm_desc desc = {0}; int ret; diff --git a/drivers/soc/qcom/scm_qcpe.c b/drivers/soc/qcom/scm_qcpe.c index 54a978157bda..1e369c73e34b 100644 --- a/drivers/soc/qcom/scm_qcpe.c +++ b/drivers/soc/qcom/scm_qcpe.c @@ -27,7 +27,7 @@ #define CREATE_TRACE_POINTS #include <trace/events/scm.h> -#include <uapi/linux/habmm.h> +#include <linux/habmm.h> #define SCM_ENOMEM (-5) #define SCM_EOPNOTSUPP (-4) @@ -218,8 +218,8 @@ static int scm_call_qcpe(u32 fn_id, struct scm_desc *desc) if (!opened) { ret = habmm_socket_open(&handle, MM_QCPE_VM1, 0, 0); - if (ret != HAB_OK) { - pr_err("scm_call2: habmm_socket_open failed with ret = %d", + if (ret) { + pr_err("scm_call_qcpe: habmm_socket_open failed with ret = %d", ret); return ret; } @@ -235,23 +235,30 @@ static int scm_call_qcpe(u32 fn_id, struct scm_desc *desc) smc_params.sid = 0; ret = habmm_socket_send(handle, &smc_params, sizeof(smc_params), 0); - if (ret != HAB_OK) + if (ret) return ret; size_bytes = sizeof(smc_params); + memset(&smc_params, 0x0, sizeof(smc_params)); ret = habmm_socket_recv(handle, &smc_params, &size_bytes, 0, 0); - if (ret != HAB_OK) + if (ret) return ret; + if (size_bytes != sizeof(smc_params)) { + pr_err("scm_call_qcpe: expected size: %lu, actual=%u\n", + sizeof(smc_params), size_bytes); + return SCM_ERROR; + } + desc->ret[0] = smc_params.x1; desc->ret[1] = smc_params.x2; desc->ret[2] = smc_params.x3; - pr_info("scm_call_qcpe: OUT: 0x%llx, 0x%llx, 0x%llx", - desc->ret[0], desc->ret[1], desc->ret[2]); + pr_info("scm_call_qcpe: OUT: 0x%llx, 0x%llx, 0x%llx, 0x%llx", + smc_params.x0, desc->ret[0], desc->ret[1], desc->ret[2]); - return 0; + return smc_params.x0; } static u32 smc(u32 cmd_addr) diff --git a/drivers/soc/qcom/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c index 4307937d9f6d..e7a00cdb5b03 100644 --- a/drivers/soc/qcom/secure_buffer.c +++ b/drivers/soc/qcom/secure_buffer.c @@ -424,13 +424,14 @@ const char *msm_secure_vmid_to_string(int secure_vmid) bool msm_secure_v2_is_supported(void) { - int version = scm_get_feat_version(FEATURE_ID_CP); + u64 version; + int ret = scm_get_feat_version(FEATURE_ID_CP, &version); /* * if the version is < 1.1.0 then dynamic buffer allocation is * not supported */ - return version >= MAKE_CP_VERSION(1, 1, 0); + return (ret == 0) && (version >= MAKE_CP_VERSION(1, 1, 0)); } static int __init alloc_secure_shared_memory(void) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index b9903fe86f60..74dbd4d42272 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -566,6 +566,10 @@ static struct msm_soc_info cpu_of_id[] = { [318] = {MSM_CPU_630, "SDM630"}, [327] = {MSM_CPU_630, "SDA630"}, + /* 636 ID */ + [345] = {MSM_CPU_636, "SDM636"}, + [346] = {MSM_CPU_636, "SDA636"}, + /* Uninitialized IDs are not known to run Linux. MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are considered as unknown CPU. */ @@ -1289,6 +1293,14 @@ static void * __init setup_dummy_socinfo(void) dummy_socinfo.id = 327; strlcpy(dummy_socinfo.build_id, "sda630 - ", sizeof(dummy_socinfo.build_id)); + } else if (early_machine_is_sdm636()) { + dummy_socinfo.id = 345; + strlcpy(dummy_socinfo.build_id, "sdm636 - ", + sizeof(dummy_socinfo.build_id)); + } else if (early_machine_is_sda636()) { + dummy_socinfo.id = 346; + strlcpy(dummy_socinfo.build_id, "sda636 - ", + sizeof(dummy_socinfo.build_id)); } else if (early_machine_is_apq8098()) { dummy_socinfo.id = 319; strlcpy(dummy_socinfo.build_id, "apq8098 - ", diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index e4afce02afc7..6c1e781d6eb9 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -196,6 +196,7 @@ struct spcom_channel { * glink state: CONNECTED / LOCAL_DISCONNECTED, REMOTE_DISCONNECTED */ unsigned glink_state; + bool is_closing; /* Events notification */ struct completion connect; @@ -208,10 +209,8 @@ struct spcom_channel { * Only one rx/tx transaction at a time (request + response). */ int ref_count; - u32 pid; - /* link UP/DOWN callback */ - void (*notify_link_state_cb)(bool up); + u32 pid; /* debug only to find user space application */ /* abort flags */ bool rx_abort; @@ -483,7 +482,17 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) switch (event) { case GLINK_CONNECTED: pr_debug("GLINK_CONNECTED, ch name [%s].\n", ch->name); + mutex_lock(&ch->lock); + + if (ch->is_closing) { + pr_err("Unexpected CONNECTED while closing [%s].\n", + ch->name); + mutex_unlock(&ch->lock); + return; + } + ch->glink_state = event; + /* * if spcom_notify_state() is called within glink_open() * then ch->glink_handle is not updated yet. @@ -493,7 +502,16 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) ch->glink_handle = handle; } - /* prepare default rx buffer after connected */ + /* signal before unlock mutex & before calling glink */ + complete_all(&ch->connect); + + /* + * Prepare default rx buffer. + * glink_queue_rx_intent() can be called only AFTER connected. + * We do it here, ASAP, to allow rx data. + */ + + pr_debug("call glink_queue_rx_intent() ch [%s].\n", ch->name); ret = glink_queue_rx_intent(ch->glink_handle, ch, ch->rx_buf_size); if (ret) { @@ -503,7 +521,9 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) ch->rx_buf_size); ch->rx_buf_ready = true; } - complete_all(&ch->connect); + + pr_debug("GLINK_CONNECTED, ch name [%s] done.\n", ch->name); + mutex_unlock(&ch->lock); break; case GLINK_LOCAL_DISCONNECTED: /* @@ -668,6 +688,13 @@ static int spcom_init_channel(struct spcom_channel *ch, const char *name) ch->glink_state = GLINK_LOCAL_DISCONNECTED; ch->actual_rx_size = 0; ch->rx_buf_size = SPCOM_RX_BUF_SIZE; + ch->is_closing = false; + ch->glink_handle = NULL; + ch->ref_count = 0; + ch->rx_abort = false; + ch->tx_abort = false; + ch->txn_id = INITIAL_TXN_ID; /* use non-zero nonce for debug */ + ch->pid = 0; return 0; } @@ -710,6 +737,7 @@ static int spcom_open(struct spcom_channel *ch, unsigned int timeout_msec) long timeleft; const char *name; void *handle; + u32 pid = current_pid(); mutex_lock(&ch->lock); name = ch->name; @@ -723,7 +751,7 @@ static int spcom_open(struct spcom_channel *ch, unsigned int timeout_msec) } pr_debug("ch [%s] opened by PID [%d], count [%d]\n", - name, ch->pid, ch->ref_count); + name, pid, ch->ref_count); pr_debug("Open channel [%s] timeout_msec [%d].\n", name, timeout_msec); @@ -738,6 +766,8 @@ static int spcom_open(struct spcom_channel *ch, unsigned int timeout_msec) /* init completion before calling glink_open() */ reinit_completion(&ch->connect); + ch->is_closing = false; + handle = glink_open(&cfg); if (IS_ERR_OR_NULL(handle)) { pr_err("glink_open failed.\n"); @@ -749,9 +779,11 @@ static int spcom_open(struct spcom_channel *ch, unsigned int timeout_msec) /* init channel context after successful open */ ch->glink_handle = handle; ch->ref_count++; - ch->pid = current_pid(); + ch->pid = pid; ch->txn_id = INITIAL_TXN_ID; + mutex_unlock(&ch->lock); + pr_debug("Wait for connection on channel [%s] timeout_msec [%d].\n", name, timeout_msec); @@ -768,8 +800,6 @@ static int spcom_open(struct spcom_channel *ch, unsigned int timeout_msec) pr_debug("Channel [%s] opened, no timeout.\n", name); } - mutex_unlock(&ch->lock); - return 0; exit_err: mutex_unlock(&ch->lock); @@ -796,6 +826,8 @@ static int spcom_close(struct spcom_channel *ch) return 0; } + ch->is_closing = true; + ret = glink_close(ch->glink_handle); if (ret) pr_err("glink_close() fail, ret [%d].\n", ret); @@ -811,6 +843,7 @@ static int spcom_close(struct spcom_channel *ch) ch->pid = 0; pr_debug("Channel closed [%s].\n", ch->name); + mutex_unlock(&ch->lock); return 0; @@ -1105,6 +1138,7 @@ struct spcom_client *spcom_register_client(struct spcom_client_info *info) ch = spcom_find_channel_by_name(name); if (!ch) { pr_err("channel %s doesn't exist, load App first.\n", name); + kfree(client); return NULL; } @@ -1292,6 +1326,7 @@ struct spcom_server *spcom_register_service(struct spcom_service_info *info) ch = spcom_find_channel_by_name(name); if (!ch) { pr_err("channel %s doesn't exist, load App first.\n", name); + kfree(server); return NULL; } @@ -2449,9 +2484,14 @@ static unsigned int spcom_device_poll(struct file *filp, done = (spcom_dev->link_state == GLINK_LINK_STATE_UP); break; case SPCOM_POLL_CH_CONNECT: + /* + * ch is not expected to be NULL since user must call open() + * to get FD before it can call poll(). + * open() will fail if no ch related to the char-device. + */ if (ch == NULL) { pr_err("invalid ch pointer, file [%s].\n", name); - return -EINVAL; + return POLLERR; } pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name); if (wait) { @@ -2752,7 +2792,7 @@ static int __init spcom_init(void) { int ret; - pr_info("spcom driver Ver 1.0 23-Nov-2015.\n"); + pr_info("spcom driver version 1.2 23-Aug-2017.\n"); ret = platform_driver_register(&spcom_driver); if (ret) diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index 745a069df88a..625030f1f256 100644 --- a/drivers/soc/qcom/watchdog_v2.c +++ b/drivers/soc/qcom/watchdog_v2.c @@ -134,6 +134,8 @@ static int msm_watchdog_suspend(struct device *dev) return 0; __raw_writel(1, wdog_dd->base + WDT0_RST); if (wdog_dd->wakeup_irq_enable) { + /* Make sure register write is complete before proceeding */ + mb(); wdog_dd->last_pet = sched_clock(); return 0; } @@ -148,8 +150,15 @@ static int msm_watchdog_resume(struct device *dev) { struct msm_watchdog_data *wdog_dd = (struct msm_watchdog_data *)dev_get_drvdata(dev); - if (!enable || wdog_dd->wakeup_irq_enable) + if (!enable) + return 0; + if (wdog_dd->wakeup_irq_enable) { + __raw_writel(1, wdog_dd->base + WDT0_RST); + /* Make sure register write is complete before proceeding */ + mb(); + wdog_dd->last_pet = sched_clock(); return 0; + } __raw_writel(1, wdog_dd->base + WDT0_EN); __raw_writel(1, wdog_dd->base + WDT0_RST); mb(); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 882cd6618cd5..87a0e47eeae6 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -107,7 +107,10 @@ static const struct file_operations dw_spi_regs_ops = { static int dw_spi_debugfs_init(struct dw_spi *dws) { - dws->debugfs = debugfs_create_dir("dw_spi", NULL); + char name[128]; + + snprintf(name, 128, "dw_spi-%s", dev_name(&dws->master->dev)); + dws->debugfs = debugfs_create_dir(name, NULL); if (!dws->debugfs) return -ENOMEM; diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index 4499dd35f2dd..da58f19dd6e6 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -1827,14 +1827,16 @@ static int msm_spi_setup(struct spi_device *spi) mb(); if (dd->pdata->is_shared) put_local_resources(dd); - /* Counter-part of system-resume when runtime-pm is not enabled. */ - if (!pm_runtime_enabled(dd->dev)) - msm_spi_pm_suspend_runtime(dd->dev); no_resources: mutex_unlock(&dd->core_lock); - pm_runtime_mark_last_busy(dd->dev); - pm_runtime_put_autosuspend(dd->dev); + /* Counter-part of system-resume when runtime-pm is not enabled. */ + if (!pm_runtime_enabled(dd->dev)) { + msm_spi_pm_suspend_runtime(dd->dev); + } else { + pm_runtime_mark_last_busy(dd->dev); + pm_runtime_put_autosuspend(dd->dev); + } err_setup_exit: return rc; diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 9f161b2631f2..e5ca8f150617 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -696,6 +696,8 @@ static const struct of_device_id spidev_dt_ids[] = { { .compatible = "rohm,dh2228fv" }, { .compatible = "lineartechnology,ltc2488" }, { .compatible = "qcom,spi-msm-codec-slave", }, + { .compatible = "nxp,mpc57xx", }, + { .compatible = "infineon,sli97", }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index b43e5656598a..a62ae9061f27 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -364,11 +364,23 @@ static int spmi_drv_remove(struct device *dev) return 0; } +static int spmi_drv_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + int ret; + + ret = of_device_uevent_modalias(dev, env); + if (ret != -ENODEV) + return ret; + + return 0; +} + static struct bus_type spmi_bus_type = { .name = "spmi", .match = spmi_device_match, .probe = spmi_drv_probe, .remove = spmi_drv_remove, + .uevent = spmi_drv_uevent, }; /** diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 07fc21797f0f..067cd58375a4 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -264,7 +264,7 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, mutex_lock(&dev->buffer_lock); ion_buffer_add(dev, buffer); mutex_unlock(&dev->buffer_lock); - atomic_add(len, &heap->total_allocated); + atomic_long_add(len, &heap->total_allocated); return buffer; err: @@ -282,7 +282,7 @@ void ion_buffer_destroy(struct ion_buffer *buffer) buffer->heap->ops->unmap_kernel(buffer->heap, buffer); buffer->heap->ops->unmap_dma(buffer->heap, buffer); - atomic_sub(buffer->size, &buffer->heap->total_allocated); + atomic_long_sub(buffer->size, &buffer->heap->total_allocated); buffer->heap->ops->free(buffer); vfree(buffer->pages); kfree(buffer); @@ -320,7 +320,7 @@ static void ion_buffer_add_to_handle(struct ion_buffer *buffer) { mutex_lock(&buffer->lock); if (buffer->handle_count == 0) - atomic_add(buffer->size, &buffer->heap->total_handles); + atomic_long_add(buffer->size, &buffer->heap->total_handles); buffer->handle_count++; mutex_unlock(&buffer->lock); @@ -346,7 +346,7 @@ static void ion_buffer_remove_from_handle(struct ion_buffer *buffer) task = current->group_leader; get_task_comm(buffer->task_comm, task); buffer->pid = task_pid_nr(task); - atomic_sub(buffer->size, &buffer->heap->total_handles); + atomic_long_sub(buffer->size, &buffer->heap->total_handles); } mutex_unlock(&buffer->lock); } @@ -1951,10 +1951,10 @@ void show_ion_usage(struct ion_device *dev) "Total orphaned size"); pr_info("---------------------------------\n"); plist_for_each_entry(heap, &dev->heaps, node) { - pr_info("%16.s 0x%16.x 0x%16.x\n", - heap->name, atomic_read(&heap->total_allocated), - atomic_read(&heap->total_allocated) - - atomic_read(&heap->total_handles)); + pr_info("%16.s 0x%16.lx 0x%16.lx\n", + heap->name, atomic_long_read(&heap->total_allocated), + atomic_long_read(&heap->total_allocated) - + atomic_long_read(&heap->total_handles)); if (heap->debug_show) heap->debug_show(heap, NULL, 0); diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c index dfb6d2f77af1..d932db4f9810 100644 --- a/drivers/staging/android/ion/ion_cma_heap.c +++ b/drivers/staging/android/ion/ion_cma_heap.c @@ -237,10 +237,12 @@ void ion_cma_heap_destroy(struct ion_heap *heap) static void ion_secure_cma_free(struct ion_buffer *buffer) { - int ret = 0; + int i, ret = 0; int source_vm; int dest_vmid; int dest_perms; + struct sg_table *sgt; + struct scatterlist *sg; struct ion_cma_buffer_info *info = buffer->priv_virt; source_vm = get_secure_vmid(buffer->flags); @@ -251,14 +253,17 @@ static void ion_secure_cma_free(struct ion_buffer *buffer) dest_vmid = VMID_HLOS; dest_perms = PERM_READ | PERM_WRITE | PERM_EXEC; - ret = hyp_assign_table(info->table, &source_vm, 1, - &dest_vmid, &dest_perms, 1); + sgt = info->table; + ret = hyp_assign_table(sgt, &source_vm, 1, &dest_vmid, &dest_perms, 1); if (ret) { pr_err("%s: Not freeing memory since assign failed\n", __func__); return; } + for_each_sg(sgt->sgl, sg, sgt->nents, i) + ClearPagePrivate(sg_page(sg)); + ion_cma_free(buffer); } @@ -266,11 +271,13 @@ static int ion_secure_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, unsigned long len, unsigned long align, unsigned long flags) { - int ret = 0; + int i, ret = 0; int source_vm; int dest_vm; int dest_perms; struct ion_cma_buffer_info *info; + struct sg_table *sgt; + struct scatterlist *sg; source_vm = VMID_HLOS; dest_vm = get_secure_vmid(flags); @@ -292,12 +299,17 @@ static int ion_secure_cma_allocate(struct ion_heap *heap, } info = buffer->priv_virt; - ret = hyp_assign_table(info->table, &source_vm, 1, - &dest_vm, &dest_perms, 1); + sgt = info->table; + ret = hyp_assign_table(sgt, &source_vm, 1, &dest_vm, &dest_perms, 1); if (ret) { pr_err("%s: Assign call failed\n", __func__); goto err; } + + /* Set the private bit to indicate that we've secured this */ + for_each_sg(sgt->sgl, sg, sgt->nents, i) + SetPagePrivate(sg_page(sg)); + return ret; err: diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h index b687675be02f..f2f2ca11c3fa 100644 --- a/drivers/staging/android/ion/ion_priv.h +++ b/drivers/staging/android/ion/ion_priv.h @@ -2,7 +2,7 @@ * drivers/staging/android/ion/ion_priv.h * * Copyright (C) 2011 Google, Inc. - * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -200,8 +200,8 @@ struct ion_heap { struct task_struct *task; int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *); - atomic_t total_allocated; - atomic_t total_handles; + atomic_long_t total_allocated; + atomic_long_t total_handles; }; /** diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 8deee007218b..9e12b2ce3272 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -537,7 +537,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) cache_limit = minfree * (long)(PAGE_SIZE / 1024); free = other_free * (long)(PAGE_SIZE / 1024); trace_lowmemory_kill(selected, cache_size, cache_limit, free); - lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \ + lowmem_print(1, "Killing '%s' (%d) (tgid %d), adj %hd,\n" \ " to free %ldkB on behalf of '%s' (%d) because\n" \ " cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \ " Free memory is %ldkB above reserved.\n" \ @@ -547,7 +547,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) " Total file cache is %ldkB\n" \ " Total zcache is %ldkB\n" \ " GFP mask is 0x%x\n", - selected->comm, selected->pid, + selected->comm, selected->pid, selected->tgid, selected_oom_score_adj, selected_tasksize * (long)(PAGE_SIZE / 1024), current->comm, current->pid, diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 7b4af519e17e..b831f08e2769 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -2901,9 +2901,6 @@ static int __init comedi_init(void) comedi_class->dev_groups = comedi_dev_groups; - /* XXX requires /proc interface */ - comedi_proc_init(); - /* create devices files for legacy/manual use */ for (i = 0; i < comedi_num_legacy_minors; i++) { struct comedi_device *dev; @@ -2911,6 +2908,7 @@ static int __init comedi_init(void) dev = comedi_alloc_board_minor(NULL); if (IS_ERR(dev)) { comedi_cleanup_board_minors(); + class_destroy(comedi_class); cdev_del(&comedi_cdev); unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS); @@ -2920,6 +2918,9 @@ static int __init comedi_init(void) mutex_unlock(&dev->mutex); } + /* XXX requires /proc interface */ + comedi_proc_init(); + return 0; } module_init(comedi_init); diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c index 82a7c27c517f..02c3feef4e36 100644 --- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c +++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c @@ -47,6 +47,7 @@ static struct usb_device_id rtw_usb_id_tbl[] = { {USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */ {USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */ {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */ + {USB_DEVICE(0x2357, 0x010c)}, /* TP-Link TL-WN722N v2 */ {USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */ {} /* Terminating entry */ }; diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 01e642db311e..f35ee85f61b5 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -529,6 +529,9 @@ static int vnt_start(struct ieee80211_hw *hw) goto free_all; } + if (vnt_key_init_table(priv)) + goto free_all; + priv->int_interval = 1; /* bInterval is set to 1 */ vnt_int_start_interrupt(priv); diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index dc1bd1f1bdfe..634ad3662ed6 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -868,6 +868,7 @@ DEF_TPG_ATTRIB(default_erl); DEF_TPG_ATTRIB(t10_pi); DEF_TPG_ATTRIB(fabric_prot_type); DEF_TPG_ATTRIB(tpg_enabled_sendtargets); +DEF_TPG_ATTRIB(login_keys_workaround); static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_attr_authentication, @@ -883,6 +884,7 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_attr_t10_pi, &iscsi_tpg_attrib_attr_fabric_prot_type, &iscsi_tpg_attrib_attr_tpg_enabled_sendtargets, + &iscsi_tpg_attrib_attr_login_keys_workaround, NULL, }; diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 9fc9117d0f22..549a2bbbf4df 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -818,7 +818,8 @@ static int iscsi_target_handle_csg_zero( SENDER_TARGET, login->rsp_buf, &login->rsp_length, - conn->param_list); + conn->param_list, + conn->tpg->tpg_attrib.login_keys_workaround); if (ret < 0) return -1; @@ -888,7 +889,8 @@ static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_log SENDER_TARGET, login->rsp_buf, &login->rsp_length, - conn->param_list); + conn->param_list, + conn->tpg->tpg_attrib.login_keys_workaround); if (ret < 0) { iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index 6d1b0acbc5b3..76bde76edad1 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -764,7 +764,8 @@ static int iscsi_check_for_auth_key(char *key) return 0; } -static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param) +static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param, + bool keys_workaround) { if (IS_TYPE_BOOL_AND(param)) { if (!strcmp(param->value, NO)) @@ -772,19 +773,31 @@ static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param) } else if (IS_TYPE_BOOL_OR(param)) { if (!strcmp(param->value, YES)) SET_PSTATE_REPLY_OPTIONAL(param); - /* - * Required for gPXE iSCSI boot client - */ - if (!strcmp(param->name, IMMEDIATEDATA)) - SET_PSTATE_REPLY_OPTIONAL(param); + + if (keys_workaround) { + /* + * Required for gPXE iSCSI boot client + */ + if (!strcmp(param->name, IMMEDIATEDATA)) + SET_PSTATE_REPLY_OPTIONAL(param); + } } else if (IS_TYPE_NUMBER(param)) { if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) SET_PSTATE_REPLY_OPTIONAL(param); - /* - * Required for gPXE iSCSI boot client - */ - if (!strcmp(param->name, MAXCONNECTIONS)) - SET_PSTATE_REPLY_OPTIONAL(param); + + if (keys_workaround) { + /* + * Required for Mellanox Flexboot PXE boot ROM + */ + if (!strcmp(param->name, FIRSTBURSTLENGTH)) + SET_PSTATE_REPLY_OPTIONAL(param); + + /* + * Required for gPXE iSCSI boot client + */ + if (!strcmp(param->name, MAXCONNECTIONS)) + SET_PSTATE_REPLY_OPTIONAL(param); + } } else if (IS_PHASE_DECLARATIVE(param)) SET_PSTATE_REPLY_OPTIONAL(param); } @@ -1421,7 +1434,8 @@ int iscsi_encode_text_output( u8 sender, char *textbuf, u32 *length, - struct iscsi_param_list *param_list) + struct iscsi_param_list *param_list, + bool keys_workaround) { char *output_buf = NULL; struct iscsi_extra_response *er; @@ -1457,7 +1471,8 @@ int iscsi_encode_text_output( *length += 1; output_buf = textbuf + *length; SET_PSTATE_PROPOSER(param); - iscsi_check_proposer_for_optional_reply(param); + iscsi_check_proposer_for_optional_reply(param, + keys_workaround); pr_debug("Sending key: %s=%s\n", param->name, param->value); } diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h index a0751e3f0813..17a58c2913f2 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.h +++ b/drivers/target/iscsi/iscsi_target_parameters.h @@ -40,7 +40,7 @@ extern int iscsi_extract_key_value(char *, char **, char **); extern int iscsi_update_param_value(struct iscsi_param *, char *); extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsi_conn *); extern int iscsi_encode_text_output(u8, u8, char *, u32 *, - struct iscsi_param_list *); + struct iscsi_param_list *, bool); extern int iscsi_check_negotiated_keys(struct iscsi_param_list *); extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *, struct iscsi_param_list *); diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 205a509b0dfb..63e1dcc5914d 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -227,6 +227,7 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg) a->t10_pi = TA_DEFAULT_T10_PI; a->fabric_prot_type = TA_DEFAULT_FABRIC_PROT_TYPE; a->tpg_enabled_sendtargets = TA_DEFAULT_TPG_ENABLED_SENDTARGETS; + a->login_keys_workaround = TA_DEFAULT_LOGIN_KEYS_WORKAROUND; } int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg) @@ -899,3 +900,21 @@ int iscsit_ta_tpg_enabled_sendtargets( return 0; } + +int iscsit_ta_login_keys_workaround( + struct iscsi_portal_group *tpg, + u32 flag) +{ + struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; + + if ((flag != 0) && (flag != 1)) { + pr_err("Illegal value %d\n", flag); + return -EINVAL; + } + + a->login_keys_workaround = flag; + pr_debug("iSCSI_TPG[%hu] - TPG enabled bit for login keys workaround: %s ", + tpg->tpgt, (a->login_keys_workaround) ? "ON" : "OFF"); + + return 0; +} diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h index 2da211920c18..901a712180f0 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.h +++ b/drivers/target/iscsi/iscsi_target_tpg.h @@ -39,5 +39,6 @@ extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32); extern int iscsit_ta_t10_pi(struct iscsi_portal_group *, u32); extern int iscsit_ta_fabric_prot_type(struct iscsi_portal_group *, u32); extern int iscsit_ta_tpg_enabled_sendtargets(struct iscsi_portal_group *, u32); +extern int iscsit_ta_login_keys_workaround(struct iscsi_portal_group *, u32); #endif /* ISCSI_TARGET_TPG_H */ diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 37c77db6e737..f71bedea973a 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -728,6 +728,15 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) if (cmd->transport_state & CMD_T_ABORTED || cmd->transport_state & CMD_T_STOP) { spin_unlock_irqrestore(&cmd->t_state_lock, flags); + /* + * If COMPARE_AND_WRITE was stopped by __transport_wait_for_tasks(), + * release se_device->caw_sem obtained by sbc_compare_and_write() + * since target_complete_ok_work() or target_complete_failure_work() + * won't be called to invoke the normal CAW completion callbacks. + */ + if (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) { + up(&dev->caw_sem); + } complete_all(&cmd->t_transport_stop_comp); return; } else if (!success) { diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 65c7033e0df0..632fa8c1260b 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -192,8 +192,10 @@ unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq) mutex_lock(&cooling_list_lock); list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { if (cpumask_test_cpu(cpu, &cpufreq_dev->allowed_cpus)) { + unsigned long level = get_level(cpufreq_dev, freq); + mutex_unlock(&cooling_list_lock); - return get_level(cpufreq_dev, freq); + return level; } } mutex_unlock(&cooling_list_lock); diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index b1f4c3f27111..50d1619d7bc3 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -200,6 +200,7 @@ static bool cluster_info_probed; static bool cluster_info_nodes_called; static bool in_suspend, retry_in_progress; static bool lmh_dcvs_available; +static bool lmh_dcvs_is_supported; static int *tsens_id_map; static int *zone_id_tsens_map; static DEFINE_MUTEX(vdd_rstr_mutex); @@ -995,7 +996,7 @@ static int msm_thermal_cpufreq_callback(struct notifier_block *nfb, switch (event) { case CPUFREQ_ADJUST: - max_freq_req = (lmh_dcvs_available) ? UINT_MAX : + max_freq_req = (lmh_dcvs_is_supported) ? UINT_MAX : cpus[policy->cpu].parent_ptr->limited_max_freq; min_freq_req = cpus[policy->cpu].parent_ptr->limited_min_freq; pr_debug("mitigating CPU%d to freq max: %u min: %u\n", @@ -2858,7 +2859,7 @@ static void msm_thermal_bite(int zone_id, int temp) tsens_id, temp); } /* If it is a secure device ignore triggering the thermal bite. */ - if (scm_is_secure_device()) + if (!scm_is_secure_device()) return; if (!is_scm_armv8()) { scm_call_atomic1(SCM_SVC_BOOT, THERM_SECURE_BITE_CMD, 0); @@ -5379,7 +5380,7 @@ int msm_thermal_init(struct msm_thermal_data *pdata) if (ret) pr_err("cannot register cpufreq notifier. err:%d\n", ret); - if (!lmh_dcvs_available) { + if (!lmh_dcvs_is_supported) { register_reboot_notifier(&msm_thermal_reboot_notifier); pm_notifier(msm_thermal_suspend_callback, 0); } @@ -7414,6 +7415,7 @@ static int msm_thermal_dev_probe(struct platform_device *pdev) if (ret) goto probe_exit; + lmh_dcvs_is_supported = of_property_read_bool(node, "clock-names"); probe_cc(node, &data, pdev); probe_freq_mitigation(node, &data, pdev); probe_cx_phase_ctrl(node, &data, pdev); diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index fc9faaee3170..b80e75fc3521 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -2255,7 +2255,7 @@ void disable_wakeup_interrupt(struct msm_hs_port *msm_uport) return; if (msm_uport->wakeup.enabled) { - disable_irq_nosync(msm_uport->wakeup.irq); + disable_irq(msm_uport->wakeup.irq); enable_irq(uport->irq); spin_lock_irqsave(&uport->lock, flags); msm_uport->wakeup.enabled = false; @@ -2614,8 +2614,7 @@ static int msm_hs_startup(struct uart_port *uport) msm_hs_resource_vote(msm_uport); if (is_use_low_power_wakeup(msm_uport)) { - ret = request_threaded_irq(msm_uport->wakeup.irq, NULL, - msm_hs_wakeup_isr, + ret = request_irq(msm_uport->wakeup.irq, msm_hs_wakeup_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "msm_hs_wakeup", msm_uport); if (unlikely(ret)) { diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 5ab54ef4f304..e4f69bddcfb1 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -2708,13 +2708,13 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) * related to the kernel should not use this. */ data = vt_get_shift_state(); - ret = __put_user(data, p); + ret = put_user(data, p); break; case TIOCL_GETMOUSEREPORTING: console_lock(); /* May be overkill */ data = mouse_reporting(); console_unlock(); - ret = __put_user(data, p); + ret = put_user(data, p); break; case TIOCL_SETVESABLANK: console_lock(); @@ -2723,7 +2723,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) break; case TIOCL_GETKMSGREDIRECT: data = vt_get_kmsg_redirect(); - ret = __put_user(data, p); + ret = put_user(data, p); break; case TIOCL_SETKMSGREDIRECT: if (!capable(CAP_SYS_ADMIN)) { diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 0b7194086c5a..df96f5f88c15 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1759,6 +1759,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + { USB_DEVICE(0xfff0, 0x0100), /* DATECS FP-2000 */ + .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ + }, { USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */ .driver_info = CLEAR_HALT_CONDITIONS, diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 96b21b0dac1e..3116edfcdc18 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -223,6 +223,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* Blackmagic Design UltraStudio SDI */ { USB_DEVICE(0x1edb, 0xbd4f), .driver_info = USB_QUIRK_NO_LPM }, + /* Hauppauge HVR-950q */ + { USB_DEVICE(0x2040, 0x7200), .driver_info = + USB_QUIRK_CONFIG_INTF_STRINGS }, + /* INTEL VALUE SSD */ { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 1b4fb562ce4b..927c84ea4921 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -39,6 +39,7 @@ #define DWC3_MSG_MAX 500 /* Global constants */ +#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */ #define DWC3_EP0_BOUNCE_SIZE 512 #define DWC3_ENDPOINTS_NUM 32 #define DWC3_XHCI_RESOURCES_NUM 2 @@ -748,6 +749,7 @@ struct dwc3_scratchpad_array { * @ctrl_req: usb control request which is used for ep0 * @ep0_trb: trb which is used for the ctrl_req * @ep0_bounce: bounce buffer for ep0 + * @zlp_buf: used when request->zero is set * @setup_buf: used while precessing STD USB requests * @ctrl_req_addr: dma address of ctrl_req * @ep0_trb: dma address of ep0_trb @@ -853,6 +855,7 @@ struct dwc3 { struct usb_ctrlrequest *ctrl_req; struct dwc3_trb *ep0_trb; void *ep0_bounce; + void *zlp_buf; void *scratchbuf; u8 *setup_buf; dma_addr_t ctrl_req_addr; diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index c2d788bc4bc5..5ad68df298cd 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -54,6 +54,8 @@ #include "debug.h" #include "xhci.h" +#define SDP_CONNETION_CHECK_TIME 10000 /* in ms */ + /* time out to wait for USB cable status notification (in ms)*/ #define SM_INIT_TIMEOUT 30000 @@ -70,6 +72,8 @@ MODULE_PARM_DESC(cpu_to_affin, "affin usb irq to this cpu"); /* XHCI registers */ #define USB3_HCSPARAMS1 (0x4) +#define USB3_HCCPARAMS2 (0x1c) +#define HCC_CTC(p) ((p) & (1 << 3)) #define USB3_PORTSC (0x420) /** @@ -211,6 +215,7 @@ struct dwc3_msm { struct notifier_block dwc3_cpu_notifier; struct notifier_block usbdev_nb; bool hc_died; + bool xhci_ss_compliance_enable; struct extcon_dev *extcon_vbus; struct extcon_dev *extcon_id; @@ -227,6 +232,7 @@ struct dwc3_msm { int pm_qos_latency; struct pm_qos_request pm_qos_req_dma; struct delayed_work perf_vote_work; + struct delayed_work sdp_check; }; #define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */ @@ -1233,7 +1239,7 @@ static void gsi_set_clear_dbell(struct usb_ep *ep, */ static bool gsi_check_ready_to_suspend(struct usb_ep *ep, bool f_suspend) { - u32 timeout = 1500; + u32 timeout = 500; u32 reg = 0; struct dwc3_ep *dep = to_dwc3_ep(ep); struct dwc3 *dwc = dep->dwc; @@ -1246,6 +1252,7 @@ static bool gsi_check_ready_to_suspend(struct usb_ep *ep, bool f_suspend) "Unable to suspend GSI ch. WR_CTRL_STATE != 0\n"); return false; } + usleep_range(20, 22); } /* Check for U3 only if we are not handling Function Suspend */ if (!f_suspend) { @@ -1927,6 +1934,7 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc) reg = dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG); if (reg & PWR_EVNT_LPM_IN_L2_MASK) break; + usleep_range(20, 30); } if (!(reg & PWR_EVNT_LPM_IN_L2_MASK)) dev_err(mdwc->dev, "could not transition HS PHY to L2\n"); @@ -2625,6 +2633,25 @@ done: return NOTIFY_DONE; } + +static void check_for_sdp_connection(struct work_struct *w) +{ + struct dwc3_msm *mdwc = + container_of(w, struct dwc3_msm, sdp_check.work); + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); + + if (!mdwc->vbus_active) + return; + + /* floating D+/D- lines detected */ + if (dwc->gadget.state < USB_STATE_DEFAULT && + dwc3_gadget_get_link_state(dwc) != DWC3_LINK_STATE_CMPLY) { + mdwc->vbus_active = 0; + dbg_event(0xFF, "Q RW SPD CHK", mdwc->vbus_active); + queue_work(mdwc->dwc3_wq, &mdwc->resume_work); + } +} + static int dwc3_msm_vbus_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { @@ -2796,6 +2823,34 @@ static ssize_t speed_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RW(speed); static void msm_dwc3_perf_vote_work(struct work_struct *w); +static ssize_t xhci_link_compliance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + + if (mdwc->xhci_ss_compliance_enable) + return snprintf(buf, PAGE_SIZE, "y\n"); + else + return snprintf(buf, PAGE_SIZE, "n\n"); +} + +static ssize_t xhci_link_compliance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + bool value; + int ret; + + ret = strtobool(buf, &value); + if (!ret) { + mdwc->xhci_ss_compliance_enable = value; + return count; + } + + return ret; +} + +static DEVICE_ATTR_RW(xhci_link_compliance); static int dwc3_msm_probe(struct platform_device *pdev) { @@ -2833,6 +2888,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) INIT_WORK(&mdwc->vbus_draw_work, dwc3_msm_vbus_draw_work); INIT_DELAYED_WORK(&mdwc->sm_work, dwc3_otg_sm_work); INIT_DELAYED_WORK(&mdwc->perf_vote_work, msm_dwc3_perf_vote_work); + INIT_DELAYED_WORK(&mdwc->sdp_check, check_for_sdp_connection); mdwc->dwc3_wq = alloc_ordered_workqueue("dwc3_wq", 0); if (!mdwc->dwc3_wq) { @@ -2844,7 +2900,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) ret = dwc3_msm_get_clk_gdsc(mdwc); if (ret) { dev_err(&pdev->dev, "error getting clock or gdsc.\n"); - return ret; + goto err; } mdwc->id_state = DWC3_ID_FLOAT; @@ -2891,8 +2947,9 @@ static int dwc3_msm_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, mdwc->ss_phy_irq, msm_dwc3_pwr_irq, msm_dwc3_pwr_irq_thread, - IRQF_TRIGGER_RISING | IRQF_EARLY_RESUME - | IRQF_ONESHOT, "ss_phy_irq", mdwc); + IRQF_TRIGGER_HIGH | IRQ_TYPE_LEVEL_HIGH + | IRQF_EARLY_RESUME | IRQF_ONESHOT, + "ss_phy_irq", mdwc); if (ret) { dev_err(&pdev->dev, "irqreq ss_phy_irq failed: %d\n", ret); @@ -3139,6 +3196,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) device_create_file(&pdev->dev, &dev_attr_mode); device_create_file(&pdev->dev, &dev_attr_speed); + device_create_file(&pdev->dev, &dev_attr_xhci_link_compliance); host_mode = usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST; if (host_mode || @@ -3151,25 +3209,21 @@ static int dwc3_msm_probe(struct platform_device *pdev) return 0; put_dwc3: - platform_device_put(mdwc->dwc3); if (mdwc->bus_perf_client) msm_bus_scale_unregister_client(mdwc->bus_perf_client); + of_platform_depopulate(&pdev->dev); err: + destroy_workqueue(mdwc->dwc3_wq); return ret; } -static int dwc3_msm_remove_children(struct device *dev, void *data) -{ - device_unregister(dev); - return 0; -} - static int dwc3_msm_remove(struct platform_device *pdev) { struct dwc3_msm *mdwc = platform_get_drvdata(pdev); int ret_pm; device_remove_file(&pdev->dev, &dev_attr_mode); + device_remove_file(&pdev->dev, &dev_attr_xhci_link_compliance); if (cpu_to_affin) unregister_cpu_notifier(&mdwc->dwc3_cpu_notifier); @@ -3199,8 +3253,7 @@ static int dwc3_msm_remove(struct platform_device *pdev) if (mdwc->hs_phy) mdwc->hs_phy->flags &= ~PHY_HOST_MODE; - platform_device_put(mdwc->dwc3); - device_for_each_child(&pdev->dev, NULL, dwc3_msm_remove_children); + of_platform_depopulate(&pdev->dev); dbg_event(0xFF, "Remov put", 0); pm_runtime_disable(mdwc->dev); @@ -3383,13 +3436,16 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) dev_dbg(mdwc->dev, "%s: turn on host\n", __func__); mdwc->hs_phy->flags |= PHY_HOST_MODE; - if (dwc->maximum_speed == USB_SPEED_SUPER) + if (dwc->maximum_speed == USB_SPEED_SUPER) { mdwc->ss_phy->flags |= PHY_HOST_MODE; + usb_phy_notify_connect(mdwc->ss_phy, + USB_SPEED_SUPER); + } + usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH); pm_runtime_get_sync(mdwc->dev); dbg_event(0xFF, "StrtHost gync", atomic_read(&mdwc->dev->power.usage_count)); - usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH); if (!IS_ERR(mdwc->vbus_reg)) ret = regulator_enable(mdwc->vbus_reg); if (ret) { @@ -3434,6 +3490,25 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) } /* + * If the Compliance Transition Capability(CTC) flag of + * HCCPARAMS2 register is set and xhci_link_compliance sysfs + * param has been enabled by the user for the SuperSpeed host + * controller, then write 10 (Link in Compliance Mode State) + * onto the Port Link State(PLS) field of the PORTSC register + * for 3.0 host controller which is at an offset of USB3_PORTSC + * + 0x10 from the DWC3 base address. Also, disable the runtime + * PM of 3.0 root hub (root hub of shared_hcd of xhci device) + */ + if (HCC_CTC(dwc3_msm_read_reg(mdwc->base, USB3_HCCPARAMS2)) + && mdwc->xhci_ss_compliance_enable + && dwc->maximum_speed == USB_SPEED_SUPER) { + dwc3_msm_write_reg(mdwc->base, USB3_PORTSC + 0x10, + 0x10340); + pm_runtime_disable(&hcd_to_xhci(platform_get_drvdata( + dwc->xhci))->shared_hcd->self.root_hub->dev); + } + + /* * In some cases it is observed that USB PHY is not going into * suspend with host mode suspend functionality. Hence disable * XHCI's runtime PM here if disable_host_mode_pm is set. @@ -3478,8 +3553,13 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) dbg_event(0xFF, "StopHost gsync", atomic_read(&mdwc->dev->power.usage_count)); usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH); + if (mdwc->ss_phy->flags & PHY_HOST_MODE) { + usb_phy_notify_disconnect(mdwc->ss_phy, + USB_SPEED_SUPER); + mdwc->ss_phy->flags &= ~PHY_HOST_MODE; + } + mdwc->hs_phy->flags &= ~PHY_HOST_MODE; - mdwc->ss_phy->flags &= ~PHY_HOST_MODE; platform_device_del(dwc->xhci); usb_unregister_notify(&mdwc->host_nb); @@ -3586,36 +3666,48 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) return 0; } -static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) +int get_psy_type(struct dwc3_msm *mdwc) { union power_supply_propval pval = {0}; - int ret; if (mdwc->charging_disabled) - return 0; - - if (mdwc->max_power == mA) - return 0; + return -EINVAL; if (!mdwc->usb_psy) { mdwc->usb_psy = power_supply_get_by_name("usb"); if (!mdwc->usb_psy) { - dev_warn(mdwc->dev, "Could not get usb power_supply\n"); + dev_err(mdwc->dev, "Could not get usb psy\n"); return -ENODEV; } } - power_supply_get_property(mdwc->usb_psy, - POWER_SUPPLY_PROP_REAL_TYPE, &pval); - if (pval.intval != POWER_SUPPLY_TYPE_USB) + power_supply_get_property(mdwc->usb_psy, POWER_SUPPLY_PROP_REAL_TYPE, + &pval); + + return pval.intval; +} + +static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) +{ + union power_supply_propval pval = {0}; + int ret, psy_type; + + if (mdwc->max_power == mA) return 0; - dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA); + psy_type = get_psy_type(mdwc); + if (psy_type == POWER_SUPPLY_TYPE_USB) { + dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA); + /* Set max current limit in uA */ + pval.intval = 1000 * mA; + } else if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) { + pval.intval = -ETIMEDOUT; + } else { + return 0; + } - /* Set max current limit in uA */ - pval.intval = 1000 * mA; ret = power_supply_set_property(mdwc->usb_psy, - POWER_SUPPLY_PROP_CURRENT_MAX, &pval); + POWER_SUPPLY_PROP_SDP_CURRENT_MAX, &pval); if (ret) { dev_dbg(mdwc->dev, "power supply error when setting property\n"); return ret; @@ -3684,6 +3776,10 @@ static void dwc3_otg_sm_work(struct work_struct *w) work = 1; } else if (test_bit(B_SESS_VLD, &mdwc->inputs)) { dev_dbg(mdwc->dev, "b_sess_vld\n"); + if (get_psy_type(mdwc) == POWER_SUPPLY_TYPE_USB_FLOAT) + queue_delayed_work(mdwc->dwc3_wq, + &mdwc->sdp_check, + msecs_to_jiffies(SDP_CONNETION_CHECK_TIME)); /* * Increment pm usage count upon cable connect. Count * is decremented in OTG_STATE_B_PERIPHERAL state on @@ -3707,6 +3803,7 @@ static void dwc3_otg_sm_work(struct work_struct *w) !test_bit(ID, &mdwc->inputs)) { dev_dbg(mdwc->dev, "!id || !bsv\n"); mdwc->otg_state = OTG_STATE_B_IDLE; + cancel_delayed_work_sync(&mdwc->sdp_check); dwc3_otg_start_peripheral(mdwc, 0); /* * Decrement pm usage count upon cable disconnect @@ -3739,6 +3836,7 @@ static void dwc3_otg_sm_work(struct work_struct *w) if (!test_bit(B_SESS_VLD, &mdwc->inputs)) { dev_dbg(mdwc->dev, "BSUSP: !bsv\n"); mdwc->otg_state = OTG_STATE_B_IDLE; + cancel_delayed_work_sync(&mdwc->sdp_check); dwc3_otg_start_peripheral(mdwc, 0); } else if (!test_bit(B_SUSPEND, &mdwc->inputs)) { dev_dbg(mdwc->dev, "BSUSP !susp\n"); diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index 5c0adb9c6fb2..81db2fa08cad 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -224,7 +224,7 @@ static int st_dwc3_probe(struct platform_device *pdev) dwc3_data->syscfg_reg_off = res->start; - dev_vdbg(&pdev->dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n", + dev_vdbg(&pdev->dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n", dwc3_data->glue_base, dwc3_data->syscfg_reg_off); dwc3_data->rstc_pwrdn = devm_reset_control_get(dev, "powerdown"); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index c2a6fdbfcfee..c244d908fa4f 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -439,6 +439,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, dwc->ep0_usb_req.request.length = sizeof(*response_pkt); dwc->ep0_usb_req.request.buf = dwc->setup_buf; dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl; + dwc->ep0_usb_req.request.dma = DMA_ERROR_CODE; return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); } @@ -729,6 +730,7 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket; dwc->ep0_usb_req.request.buf = dwc->setup_buf; dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl; + dwc->ep0_usb_req.request.dma = DMA_ERROR_CODE; return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 658fcca485d8..94709587f238 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1340,6 +1340,32 @@ static bool dwc3_gadget_is_suspended(struct dwc3 *dwc) return false; } +static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep, + struct usb_request *request) +{ + dwc3_gadget_ep_free_request(ep, request); +} + +static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_request *req; + struct usb_request *request; + struct usb_ep *ep = &dep->endpoint; + + dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n"); + request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC); + if (!request) + return -ENOMEM; + + request->length = 0; + request->buf = dwc->zlp_buf; + request->complete = __dwc3_gadget_ep_zlp_complete; + + req = to_dwc3_request(request); + + return __dwc3_gadget_ep_queue(dep, req); +} + static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags) { @@ -1387,6 +1413,16 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, ret = __dwc3_gadget_ep_queue(dep, req); + /* + * Okay, here's the thing, if gadget driver has requested for a ZLP by + * setting request->zero, instead of doing magic, we will just queue an + * extra usb_request ourselves so that it gets handled the same way as + * any other request. + */ + if (ret == 0 && request->zero && request->length && + (request->length % ep->maxpacket == 0)) + ret = __dwc3_gadget_ep_queue_zlp(dwc, dep); + out: spin_unlock_irqrestore(&dwc->lock, flags); @@ -3554,6 +3590,12 @@ int dwc3_gadget_init(struct dwc3 *dwc) goto err3; } + dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL); + if (!dwc->zlp_buf) { + ret = -ENOMEM; + goto err4; + } + dwc->gadget.ops = &dwc3_gadget_ops; dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->gadget.sg_supported = true; @@ -3595,12 +3637,12 @@ int dwc3_gadget_init(struct dwc3 *dwc) ret = dwc3_gadget_init_endpoints(dwc); if (ret) - goto err4; + goto err5; ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); if (ret) { dev_err(dwc->dev, "failed to register udc\n"); - goto err4; + goto err5; } if (!dwc->is_drd) { @@ -3612,6 +3654,9 @@ int dwc3_gadget_init(struct dwc3 *dwc) return 0; +err5: + kfree(dwc->zlp_buf); + err4: dwc3_gadget_free_endpoints(dwc); dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE, @@ -3649,6 +3694,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dwc->ep0_bounce, dwc->ep0_bounce_addr); kfree(dwc->setup_buf); + kfree(dwc->zlp_buf); dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2, dwc->ep0_trb, dwc->ep0_trb_addr); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index eeccae8bfc1b..442d44278f33 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -35,7 +35,8 @@ (speed == USB_SPEED_SUPER ?\ SSUSB_GADGET_VBUS_DRAW : CONFIG_USB_GADGET_VBUS_DRAW) -static bool disable_l1_for_hs; +/* disable LPM by default */ +static bool disable_l1_for_hs = true; module_param(disable_l1_for_hs, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(disable_l1_for_hs, "Disable support for L1 LPM for HS devices"); diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index 1ef3442cf618..7950f25136a5 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -346,6 +346,7 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) struct acc_dev *dev = ep->driver_data; char *string_dest = NULL; int length = req->actual; + unsigned long flags; if (req->status != 0) { pr_err("acc_complete_set_string, err %d\n", req->status); @@ -371,22 +372,26 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) case ACCESSORY_STRING_SERIAL: string_dest = dev->serial; break; + default: + pr_err("unknown accessory string index %d\n", + dev->string_index); + return; } - if (string_dest) { - unsigned long flags; - if (length >= ACC_STRING_SIZE) - length = ACC_STRING_SIZE - 1; - - spin_lock_irqsave(&dev->lock, flags); - memcpy(string_dest, req->buf, length); - /* ensure zero termination */ - string_dest[length] = 0; - spin_unlock_irqrestore(&dev->lock, flags); - } else { - pr_err("unknown accessory string index %d\n", - dev->string_index); + if (!length) { + pr_debug("zero length for accessory string index %d\n", + dev->string_index); + return; } + + if (length >= ACC_STRING_SIZE) + length = ACC_STRING_SIZE - 1; + + spin_lock_irqsave(&dev->lock, flags); + memcpy(string_dest, req->buf, length); + /* ensure zero termination */ + string_dest[length] = 0; + spin_unlock_irqrestore(&dev->lock, flags); } static void acc_complete_set_hid_report_desc(struct usb_ep *ep, diff --git a/drivers/usb/gadget/function/f_ccid.c b/drivers/usb/gadget/function/f_ccid.c index 28ac8d0010d8..1a281833eadd 100644 --- a/drivers/usb/gadget/function/f_ccid.c +++ b/drivers/usb/gadget/function/f_ccid.c @@ -26,7 +26,7 @@ #include "f_ccid.h" #define BULK_IN_BUFFER_SIZE sizeof(struct ccid_bulk_in_header) -#define BULK_OUT_BUFFER_SIZE sizeof(struct ccid_bulk_out_header) +#define BULK_OUT_BUFFER_SIZE 1024 #define CTRL_BUF_SIZE 4 #define FUNCTION_NAME "ccid" #define MAX_INST_NAME_LEN 40 @@ -629,14 +629,14 @@ static ssize_t ccid_bulk_read(struct file *fp, char __user *buf, struct f_ccid *ccid_dev = fp->private_data; struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev; struct usb_request *req; - int r = count, xfer; + int r = count, xfer, len; int ret; unsigned long flags; pr_debug("ccid_bulk_read(%zu)\n", count); if (count > BULK_OUT_BUFFER_SIZE) { - pr_err("%s: max_buffer_size:%zu given_pkt_size:%zu\n", + pr_err("%s: max_buffer_size:%d given_pkt_size:%zu\n", __func__, BULK_OUT_BUFFER_SIZE, count); return -ENOMEM; } @@ -647,6 +647,7 @@ static ssize_t ccid_bulk_read(struct file *fp, char __user *buf, goto done; } + len = ALIGN(count, ccid_dev->out->maxpacket); requeue_req: spin_lock_irqsave(&ccid_dev->lock, flags); if (!atomic_read(&ccid_dev->online)) { @@ -655,7 +656,7 @@ requeue_req: } /* queue a request */ req = bulk_dev->rx_req; - req->length = count; + req->length = len; bulk_dev->rx_done = 0; spin_unlock_irqrestore(&ccid_dev->lock, flags); ret = usb_ep_queue(ccid_dev->out, req, GFP_KERNEL); @@ -688,6 +689,9 @@ requeue_req: spin_unlock_irqrestore(&ccid_dev->lock, flags); goto requeue_req; } + if (req->actual > count) + pr_err("%s More data received(%d) than required(%zu)\n", + __func__, req->actual, count); xfer = (req->actual < count) ? req->actual : count; atomic_set(&bulk_dev->rx_req_busy, 1); spin_unlock_irqrestore(&ccid_dev->lock, flags); @@ -875,7 +879,8 @@ static ssize_t ccid_ctrl_read(struct file *fp, char __user *buf, count = CTRL_BUF_SIZE; ret = wait_event_interruptible(ctrl_dev->tx_wait_q, - ctrl_dev->tx_ctrl_done); + ctrl_dev->tx_ctrl_done || + !atomic_read(&ccid_dev->online)); if (ret < 0) return ret; ctrl_dev->tx_ctrl_done = 0; diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 3f903d4776b4..19fe6c8cb25a 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -1579,6 +1579,12 @@ static void gsi_rndis_command_complete(struct usb_ep *ep, struct f_gsi *rndis = req->context; int status; + if (req->status != 0) { + log_event_err("RNDIS command completion error %d\n", + req->status); + return; + } + status = rndis_msg_parser(rndis->params, (u8 *) req->buf); if (status < 0) log_event_err("RNDIS command error %d, %d/%d", diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 99285b416308..ee579ba2b59e 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -539,7 +539,7 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } status = usb_ep_enable(hidg->out_ep); if (status < 0) { - ERROR(cdev, "Enable IN endpoint FAILED!\n"); + ERROR(cdev, "Enable OUT endpoint FAILED!\n"); goto fail; } hidg->out_ep->driver_data = hidg; diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c index 434af820e827..a28bcd084dc3 100644 --- a/drivers/usb/gadget/function/f_qc_rndis.c +++ b/drivers/usb/gadget/function/f_qc_rndis.c @@ -545,6 +545,12 @@ static void rndis_qc_command_complete(struct usb_ep *ep, rndis_init_msg_type *buf; u32 ul_max_xfer_size, dl_max_xfer_size; + if (req->status != 0) { + pr_err("%s: RNDIS command completion error %d\n", + __func__, req->status); + return; + } + spin_lock(&rndis_lock); rndis = _rndis_qc; if (!rndis || !rndis->notify || !rndis->notify->driver_data) { diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 777acb489875..8536f10a2e35 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -183,15 +183,28 @@ static struct usb_qdss_opts *to_fi_usb_qdss_opts(struct usb_function_instance *f } /*----------------------------------------------------------------------*/ -static void qdss_ctrl_write_complete(struct usb_ep *ep, +static void qdss_write_complete(struct usb_ep *ep, struct usb_request *req) { struct f_qdss *qdss = ep->driver_data; struct qdss_request *d_req = req->context; + struct usb_ep *in; + struct list_head *list_pool; + enum qdss_state state; unsigned long flags; pr_debug("qdss_ctrl_write_complete\n"); + if (qdss->debug_inface_enabled) { + in = qdss->port.ctrl_in; + list_pool = &qdss->ctrl_write_pool; + state = USB_QDSS_CTRL_WRITE_DONE; + } else { + in = qdss->port.data; + list_pool = &qdss->data_write_pool; + state = USB_QDSS_DATA_WRITE_DONE; + } + if (!req->status) { /* send zlp */ if ((req->length >= ep->maxpacket) && @@ -199,13 +212,13 @@ static void qdss_ctrl_write_complete(struct usb_ep *ep, req->length = 0; d_req->actual = req->actual; d_req->status = req->status; - if (!usb_ep_queue(qdss->port.ctrl_in, req, GFP_ATOMIC)) + if (!usb_ep_queue(in, req, GFP_ATOMIC)) return; } } spin_lock_irqsave(&qdss->lock, flags); - list_add_tail(&req->list, &qdss->ctrl_write_pool); + list_add_tail(&req->list, list_pool); if (req->length != 0) { d_req->actual = req->actual; d_req->status = req->status; @@ -213,8 +226,7 @@ static void qdss_ctrl_write_complete(struct usb_ep *ep, spin_unlock_irqrestore(&qdss->lock, flags); if (qdss->ch.notify) - qdss->ch.notify(qdss->ch.priv, USB_QDSS_CTRL_WRITE_DONE, d_req, - NULL); + qdss->ch.notify(qdss->ch.priv, state, d_req, NULL); } static void qdss_ctrl_read_complete(struct usb_ep *ep, @@ -252,6 +264,12 @@ void usb_qdss_free_req(struct usb_qdss_ch *ch) return; } + list_for_each_safe(act, tmp, &qdss->data_write_pool) { + req = list_entry(act, struct usb_request, list); + list_del(&req->list); + usb_ep_free_request(qdss->port.data, req); + } + list_for_each_safe(act, tmp, &qdss->ctrl_write_pool) { req = list_entry(act, struct usb_request, list); list_del(&req->list); @@ -271,23 +289,41 @@ int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf, { struct f_qdss *qdss = ch->priv_usb; struct usb_request *req; + struct usb_ep *in; + struct list_head *list_pool; int i; pr_debug("usb_qdss_alloc_req\n"); - if (no_write_buf <= 0 || no_read_buf <= 0 || !qdss) { + if (!qdss) { + pr_err("usb_qdss_alloc_req: channel %s closed\n", ch->name); + return -ENODEV; + } + + if ((qdss->debug_inface_enabled && + (no_write_buf <= 0 || no_read_buf <= 0)) || + (!qdss->debug_inface_enabled && + (no_write_buf <= 0 || no_read_buf))) { pr_err("usb_qdss_alloc_req: missing params\n"); return -ENODEV; } + if (qdss->debug_inface_enabled) { + in = qdss->port.ctrl_in; + list_pool = &qdss->ctrl_write_pool; + } else { + in = qdss->port.data; + list_pool = &qdss->data_write_pool; + } + for (i = 0; i < no_write_buf; i++) { - req = usb_ep_alloc_request(qdss->port.ctrl_in, GFP_ATOMIC); + req = usb_ep_alloc_request(in, GFP_ATOMIC); if (!req) { pr_err("usb_qdss_alloc_req: ctrl_in allocation err\n"); goto fail; } - req->complete = qdss_ctrl_write_complete; - list_add_tail(&req->list, &qdss->ctrl_write_pool); + req->complete = qdss_write_complete; + list_add_tail(&req->list, list_pool); } for (i = 0; i < no_read_buf; i++) { @@ -479,21 +515,20 @@ static void usb_qdss_disconnect_work(struct work_struct *work) qdss = container_of(work, struct f_qdss, disconnect_w); pr_debug("usb_qdss_disconnect_work\n"); - /* - * Uninitialized init data i.e. ep specific operation. - * Notify qdss to cancel all active transfers. - */ - if (qdss->ch.app_conn) { + + /* Notify qdss to cancel all active transfers */ + if (qdss->ch.notify) + qdss->ch.notify(qdss->ch.priv, + USB_QDSS_DISCONNECT, + NULL, + NULL); + + /* Uninitialized init data i.e. ep specific operation */ + if (qdss->ch.app_conn && !strcmp(qdss->ch.name, USB_QDSS_CH_MSM)) { status = uninit_data(qdss->port.data); if (status) pr_err("%s: uninit_data error\n", __func__); - if (qdss->ch.notify) - qdss->ch.notify(qdss->ch.priv, - USB_QDSS_DISCONNECT, - NULL, - NULL); - status = set_qdss_data_connection(qdss, 0); if (status) pr_err("qdss_disconnect error"); @@ -550,15 +585,16 @@ static void usb_qdss_connect_work(struct work_struct *work) } pr_debug("usb_qdss_connect_work\n"); + + if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) + goto notify; + status = set_qdss_data_connection(qdss, 1); if (status) { pr_err("set_qdss_data_connection error(%d)", status); return; } - if (qdss->ch.notify) - qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT, - NULL, &qdss->ch); spin_lock_irqsave(&qdss->lock, flags); req = qdss->endless_req; spin_unlock_irqrestore(&qdss->lock, flags); @@ -566,8 +602,15 @@ static void usb_qdss_connect_work(struct work_struct *work) return; status = usb_ep_queue(qdss->port.data, req, GFP_ATOMIC); - if (status) + if (status) { pr_err("%s: usb_ep_queue error (%d)\n", __func__, status); + return; + } + +notify: + if (qdss->ch.notify) + qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT, + NULL, &qdss->ch); } static int qdss_set_alt(struct usb_function *f, unsigned intf, unsigned alt) @@ -706,6 +749,7 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name) spin_lock_init(&qdss->lock); INIT_LIST_HEAD(&qdss->ctrl_read_pool); INIT_LIST_HEAD(&qdss->ctrl_write_pool); + INIT_LIST_HEAD(&qdss->data_write_pool); INIT_WORK(&qdss->connect_w, usb_qdss_connect_work); INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work); @@ -801,6 +845,50 @@ int usb_qdss_ctrl_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) } EXPORT_SYMBOL(usb_qdss_ctrl_write); +int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) +{ + struct f_qdss *qdss = ch->priv_usb; + unsigned long flags; + struct usb_request *req = NULL; + + pr_debug("usb_qdss_ctrl_write\n"); + + if (!qdss) + return -ENODEV; + + spin_lock_irqsave(&qdss->lock, flags); + + if (qdss->usb_connected == 0) { + spin_unlock_irqrestore(&qdss->lock, flags); + return -EIO; + } + + if (list_empty(&qdss->data_write_pool)) { + pr_err("error: usb_qdss_data_write list is empty\n"); + spin_unlock_irqrestore(&qdss->lock, flags); + return -EAGAIN; + } + + req = list_first_entry(&qdss->data_write_pool, struct usb_request, + list); + list_del(&req->list); + spin_unlock_irqrestore(&qdss->lock, flags); + + req->buf = d_req->buf; + req->length = d_req->length; + req->context = d_req; + if (usb_ep_queue(qdss->port.data, req, GFP_ATOMIC)) { + spin_lock_irqsave(&qdss->lock, flags); + list_add_tail(&req->list, &qdss->data_write_pool); + spin_unlock_irqrestore(&qdss->lock, flags); + pr_err("qdss usb_ep_queue failed\n"); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(usb_qdss_write); + struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv, void (*notify)(void *, unsigned, struct qdss_request *, struct usb_qdss_ch *)) @@ -859,7 +947,9 @@ void usb_qdss_close(struct usb_qdss_ch *ch) pr_debug("usb_qdss_close\n"); spin_lock_irqsave(&qdss_lock, flags); - if (!qdss || !qdss->usb_connected) { + ch->priv_usb = NULL; + if (!qdss || !qdss->usb_connected || + !strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) { ch->app_conn = 0; spin_unlock_irqrestore(&qdss_lock, flags); return; diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h index cad0f4cc06f9..fb7c01c0f939 100644 --- a/drivers/usb/gadget/function/f_qdss.h +++ b/drivers/usb/gadget/function/f_qdss.h @@ -53,6 +53,10 @@ struct f_qdss { struct usb_qdss_ch ch; struct list_head ctrl_read_pool; struct list_head ctrl_write_pool; + + /* for mdm channel SW path */ + struct list_head data_write_pool; + struct work_struct connect_w; struct work_struct disconnect_w; spinlock_t lock; diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 13888821109d..0917bc500023 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -463,6 +463,12 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) int status; rndis_init_msg_type *buf; + if (req->status != 0) { + pr_err("%s: RNDIS command completion error:%d\n", + __func__, req->status); + return; + } + /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ // spin_lock(&dev->lock); status = rndis_msg_parser(rndis->params, (u8 *) req->buf); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 9dbd7595a7a3..fae1222d4bc8 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -783,6 +783,9 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, clear_bit(wIndex, &bus_state->resuming_ports); set_bit(wIndex, &bus_state->rexit_ports); + + xhci_test_and_clear_bit(xhci, port_array, wIndex, + PORT_PLC); xhci_set_link_state(xhci, port_array, wIndex, XDEV_U0); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 12e38cf022ff..5bfe47816ac3 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -856,13 +856,16 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci, (ep->ep_state & EP_GETTING_NO_STREAMS)) { int stream_id; - for (stream_id = 0; stream_id < ep->stream_info->num_streams; + for (stream_id = 1; stream_id < ep->stream_info->num_streams; stream_id++) { + ring = ep->stream_info->stream_rings[stream_id]; + if (!ring) + continue; + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Killing URBs for slot ID %u, ep index %u, stream %u", - slot_id, ep_index, stream_id + 1); - xhci_kill_ring_urbs(xhci, - ep->stream_info->stream_rings[stream_id]); + slot_id, ep_index, stream_id); + xhci_kill_ring_urbs(xhci, ring); } } else { ring = ep->ring; diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 7cdc95d2b085..f9f47da8a88b 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -481,21 +481,18 @@ static inline void pd_reset_protocol(struct usbpd *pd) pd->send_dr_swap = false; } -static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data, - size_t num_data, enum pd_msg_type type) +static int pd_send_msg(struct usbpd *pd, u8 msg_type, const u32 *data, + size_t num_data, enum pd_sop_type sop) { int ret; u16 hdr; - hdr = PD_MSG_HDR(hdr_type, pd->current_dr, pd->current_pr, + hdr = PD_MSG_HDR(msg_type, pd->current_dr, pd->current_pr, pd->tx_msgid, num_data, pd->spec_rev); - ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), type, 15); - /* TODO figure out timeout. based on tReceive=1.1ms x nRetryCount? */ - if (ret < 0) + ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), sop); + if (ret) return ret; - else if (ret != num_data * sizeof(u32)) - return -EIO; pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; return 0; @@ -596,7 +593,7 @@ static void pd_send_hard_reset(struct usbpd *pd) /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); pd->hard_reset_count++; - pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */ + pd_phy_signal(HARD_RESET_SIG); pd->in_pr_swap = false; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); } @@ -612,12 +609,12 @@ static void kick_sm(struct usbpd *pd, int ms) queue_work(pd->wq, &pd->sm_work); } -static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) +static void phy_sig_received(struct usbpd *pd, enum pd_sig_type sig) { union power_supply_propval val = {1}; - if (type != HARD_RESET_SIG) { - usbpd_err(&pd->dev, "invalid signal (%d) received\n", type); + if (sig != HARD_RESET_SIG) { + usbpd_err(&pd->dev, "invalid signal (%d) received\n", sig); return; } @@ -632,16 +629,16 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) kick_sm(pd, 0); } -static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, +static void phy_msg_received(struct usbpd *pd, enum pd_sop_type sop, u8 *buf, size_t len) { struct rx_msg *rx_msg; unsigned long flags; u16 header; - if (type != SOP_MSG) { + if (sop != SOP_MSG) { usbpd_err(&pd->dev, "invalid msg type (%d) received; only SOP supported\n", - type); + sop); return; } @@ -920,6 +917,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) if (pd->psy_type == POWER_SUPPLY_TYPE_USB || pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP || + pd->psy_type == POWER_SUPPLY_TYPE_USB_FLOAT || usb_compliance_mode) start_usb_peripheral(pd); } @@ -963,7 +961,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->current_voltage = pd->requested_voltage = 5000000; val.intval = pd->requested_voltage; /* set max range to 5V */ power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_VOLTAGE_MAX, &val); + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, &val); if (!pd->vbus_present) { pd->current_state = PE_SNK_DISCOVERY; @@ -1616,7 +1614,6 @@ static void usbpd_sm(struct work_struct *w) else if (pd->current_dr == DR_DFP) stop_usb_host(pd); - pd->current_pr = PR_NONE; pd->current_dr = DR_NONE; reset_vdm_state(pd); @@ -1662,7 +1659,7 @@ static void usbpd_sm(struct work_struct *w) pd->requested_voltage = 5000000; val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_VOLTAGE_MIN, &val); + POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, &val); pd->in_pr_swap = false; val.intval = 0; @@ -1701,7 +1698,6 @@ static void usbpd_sm(struct work_struct *w) if (pd->current_pr == PR_SINK) { usbpd_set_state(pd, PE_SNK_STARTUP); } else if (pd->current_pr == PR_SRC) { - enable_vbus(pd); if (!pd->vconn_enabled && pd->typec_mode == POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE) { @@ -1711,6 +1707,7 @@ static void usbpd_sm(struct work_struct *w) else pd->vconn_enabled = true; } + enable_vbus(pd); usbpd_set_state(pd, PE_SRC_STARTUP); } @@ -1981,8 +1978,8 @@ static void usbpd_sm(struct work_struct *w) val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, pd->requested_voltage >= pd->current_voltage ? - POWER_SUPPLY_PROP_VOLTAGE_MAX : - POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX : + POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, &val); /* @@ -2033,8 +2030,8 @@ static void usbpd_sm(struct work_struct *w) val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, pd->requested_voltage >= pd->current_voltage ? - POWER_SUPPLY_PROP_VOLTAGE_MIN : - POWER_SUPPLY_PROP_VOLTAGE_MAX, &val); + POWER_SUPPLY_PROP_PD_VOLTAGE_MIN : + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, &val); pd->current_voltage = pd->requested_voltage; /* resume charging */ @@ -2216,7 +2213,7 @@ static void usbpd_sm(struct work_struct *w) val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_VOLTAGE_MIN, &val); + POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, &val); pd_send_hard_reset(pd); pd->in_explicit_contract = false; @@ -2479,6 +2476,16 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) if (pd->current_pr == PR_SINK) return 0; + /* + * Unexpected if not in PR swap; need to force disconnect from + * source so we can turn off VBUS, Vconn, PD PHY etc. + */ + if (pd->current_pr == PR_SRC) { + usbpd_info(&pd->dev, "Forcing disconnect from source mode\n"); + pd->current_pr = PR_NONE; + break; + } + pd->current_pr = PR_SINK; break; diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c index e200c25bc23a..b454442e2471 100644 --- a/drivers/usb/pd/qpnp-pdphy.c +++ b/drivers/usb/pd/qpnp-pdphy.c @@ -80,6 +80,10 @@ #define VDD_PDPHY_VOL_MAX 3300000 /* uV */ #define VDD_PDPHY_HPM_LOAD 3000 /* uA */ +/* timers */ +#define RECEIVER_RESPONSE_TIME 15 /* tReceiverResponse */ +#define HARD_RESET_COMPLETE_TIME 5 /* tHardResetComplete */ + struct usb_pdphy { struct device *dev; struct regmap *regmap; @@ -96,8 +100,8 @@ struct usb_pdphy { int msg_tx_discarded_irq; int msg_rx_discarded_irq; - void (*signal_cb)(struct usbpd *pd, enum pd_sig_type type); - void (*msg_rx_cb)(struct usbpd *pd, enum pd_msg_type type, + void (*signal_cb)(struct usbpd *pd, enum pd_sig_type sig); + void (*msg_rx_cb)(struct usbpd *pd, enum pd_sop_type sop, u8 *buf, size_t len); void (*shutdown_cb)(struct usbpd *pd); @@ -401,14 +405,13 @@ int pd_phy_open(struct pd_phy_params *params) } EXPORT_SYMBOL(pd_phy_open); -int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms) +int pd_phy_signal(enum pd_sig_type sig) { u8 val; int ret; struct usb_pdphy *pdphy = __pdphy; - dev_dbg(pdphy->dev, "%s: type %d timeout %u\n", __func__, type, - timeout_ms); + dev_dbg(pdphy->dev, "%s: type %d\n", __func__, sig); if (!pdphy) { pr_err("%s: pdphy not found\n", __func__); @@ -428,7 +431,7 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms) usleep_range(2, 3); - val = (type == CABLE_RESET_SIG ? TX_CONTROL_FRAME_TYPE_CABLE_RESET : 0) + val = (sig == CABLE_RESET_SIG ? TX_CONTROL_FRAME_TYPE_CABLE_RESET : 0) | TX_CONTROL_SEND_SIGNAL; ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, val); @@ -436,7 +439,8 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms) return ret; ret = wait_event_interruptible_timeout(pdphy->tx_waitq, - pdphy->tx_status != -EINPROGRESS, msecs_to_jiffies(timeout_ms)); + pdphy->tx_status != -EINPROGRESS, + msecs_to_jiffies(HARD_RESET_COMPLETE_TIME)); if (ret <= 0) { dev_err(pdphy->dev, "%s: failed ret %d", __func__, ret); return ret ? ret : -ETIMEDOUT; @@ -447,7 +451,7 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms) if (pdphy->tx_status) return pdphy->tx_status; - if (type == HARD_RESET_SIG) + if (sig == HARD_RESET_SIG) /* Frame filter is reconfigured in pd_phy_open() */ return pdphy_reg_write(pdphy, USB_PDPHY_FRAME_FILTER, 0); @@ -455,16 +459,15 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms) } EXPORT_SYMBOL(pd_phy_signal); -int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, - enum pd_msg_type type, unsigned int timeout_ms) +int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, enum pd_sop_type sop) { u8 val; int ret; size_t total_len = data_len + USB_PDPHY_MSG_HDR_LEN; struct usb_pdphy *pdphy = __pdphy; - dev_dbg(pdphy->dev, "%s: hdr %x frame type %d timeout %u\n", - __func__, hdr, type, timeout_ms); + dev_dbg(pdphy->dev, "%s: hdr %x frame sop_type %d\n", + __func__, hdr, sop); if (data && data_len) print_hex_dump_debug("tx data obj:", DUMP_PREFIX_NONE, 32, 4, @@ -518,14 +521,15 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, usleep_range(2, 3); - val = TX_CONTROL_RETRY_COUNT | (type << 2) | TX_CONTROL_SEND_MSG; + val = TX_CONTROL_RETRY_COUNT | (sop << 2) | TX_CONTROL_SEND_MSG; ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, val); if (ret) return ret; ret = wait_event_interruptible_timeout(pdphy->tx_waitq, - pdphy->tx_status != -EINPROGRESS, msecs_to_jiffies(timeout_ms)); + pdphy->tx_status != -EINPROGRESS, + msecs_to_jiffies(RECEIVER_RESPONSE_TIME)); if (ret <= 0) { dev_err(pdphy->dev, "%s: failed ret %d", __func__, ret); return ret ? ret : -ETIMEDOUT; @@ -534,7 +538,7 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, if (hdr && !pdphy->tx_status) pdphy->tx_bytes += data_len + USB_PDPHY_MSG_HDR_LEN; - return pdphy->tx_status ? pdphy->tx_status : data_len; + return pdphy->tx_status ? pdphy->tx_status : 0; } EXPORT_SYMBOL(pd_phy_write); diff --git a/drivers/usb/pd/usbpd.h b/drivers/usb/pd/usbpd.h index 108701739f89..9b6053e940e9 100644 --- a/drivers/usb/pd/usbpd.h +++ b/drivers/usb/pd/usbpd.h @@ -45,7 +45,7 @@ enum pd_sig_type { CABLE_RESET_SIG, }; -enum pd_msg_type { +enum pd_sop_type { SOP_MSG = 0, SOPI_MSG, SOPII_MSG, @@ -61,8 +61,8 @@ enum pd_spec_rev { #define FRAME_FILTER_EN_HARD_RESET BIT(5) struct pd_phy_params { - void (*signal_cb)(struct usbpd *pd, enum pd_sig_type type); - void (*msg_rx_cb)(struct usbpd *pd, enum pd_msg_type type, + void (*signal_cb)(struct usbpd *pd, enum pd_sig_type sig); + void (*msg_rx_cb)(struct usbpd *pd, enum pd_sop_type sop, u8 *buf, size_t len); void (*shutdown_cb)(struct usbpd *pd); enum data_role data_role; @@ -72,9 +72,9 @@ struct pd_phy_params { #if IS_ENABLED(CONFIG_QPNP_USB_PDPHY) int pd_phy_open(struct pd_phy_params *params); -int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms); +int pd_phy_signal(enum pd_sig_type sig); int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, - enum pd_msg_type type, unsigned int timeout_ms); + enum pd_sop_type sop); int pd_phy_update_roles(enum data_role dr, enum power_role pr); void pd_phy_close(void); #else @@ -83,13 +83,13 @@ static inline int pd_phy_open(struct pd_phy_params *params) return -ENODEV; } -static inline int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms) +static inline int pd_phy_signal(enum pd_sig_type type) { return -ENODEV; } static inline int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, - enum pd_msg_type type, unsigned int timeout_ms) + enum pd_sop_type sop) { return -ENODEV; } diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index bd2722e8fc48..be63c6c0a86a 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -74,10 +74,6 @@ #define QUSB2PHY_PORT_TUNE4 0x8C #define QUSB2PHY_PORT_TUNE5 0x90 -/* In case Efuse register shows zero, use this value */ -#define TUNE2_DEFAULT_HIGH_NIBBLE 0xB -#define TUNE2_DEFAULT_LOW_NIBBLE 0x3 - /* Get TUNE2's high nibble value read from efuse */ #define TUNE2_HIGH_NIBBLE_VAL(val, pos, mask) ((val >> pos) & mask) @@ -147,6 +143,7 @@ struct qusb_phy { u32 tune2_val; int tune2_efuse_bit_pos; int tune2_efuse_num_of_bits; + int tune2_efuse_correction; bool power_enabled; bool clocks_enabled; @@ -433,6 +430,7 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) { u8 num_of_bits; u32 bit_mask = 1; + u8 reg_val; pr_debug("%s(): num_of_bits:%d bit_pos:%d\n", __func__, qphy->tune2_efuse_num_of_bits, @@ -446,9 +444,8 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) /* * Read EFUSE register having TUNE2 parameter's high nibble. - * If efuse register shows value as 0x0, then use default value - * as 0xB as high nibble. Otherwise use efuse register based - * value for this purpose. + * If efuse register shows value as 0x0, then use previous value + * as it is. Otherwise use efuse register based value for this purpose. */ qphy->tune2_val = readl_relaxed(qphy->tune2_efuse_reg); pr_debug("%s(): bit_mask:%d efuse based tune2 value:%d\n", @@ -457,12 +454,24 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) qphy->tune2_val = TUNE2_HIGH_NIBBLE_VAL(qphy->tune2_val, qphy->tune2_efuse_bit_pos, bit_mask); - if (!qphy->tune2_val) - qphy->tune2_val = TUNE2_DEFAULT_HIGH_NIBBLE; + /* Update higher nibble of TUNE2 value for better rise/fall times */ + if (qphy->tune2_efuse_correction && qphy->tune2_val) { + if (qphy->tune2_efuse_correction > 5 || + qphy->tune2_efuse_correction < -10) + pr_warn("Correction value is out of range : %d\n", + qphy->tune2_efuse_correction); + else + qphy->tune2_val = qphy->tune2_val + + qphy->tune2_efuse_correction; + } + + reg_val = readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE2); + if (qphy->tune2_val) { + reg_val &= 0x0f; + reg_val |= (qphy->tune2_val << 4); + } - /* Get TUNE2 byte value using high and low nibble value */ - qphy->tune2_val = ((qphy->tune2_val << 0x4) | - TUNE2_DEFAULT_LOW_NIBBLE); + qphy->tune2_val = reg_val; } static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt, @@ -570,7 +579,7 @@ static int qusb_phy_init(struct usb_phy *phy) * and try to read EFUSE value only once i.e. not every USB * cable connect case. */ - if (qphy->tune2_efuse_reg) { + if (qphy->tune2_efuse_reg && !tune2) { if (!qphy->tune2_val) qusb_phy_get_tune2_param(qphy); @@ -929,6 +938,9 @@ static int qusb_phy_probe(struct platform_device *pdev) "qcom,tune2-efuse-num-bits", &qphy->tune2_efuse_num_of_bits); } + of_property_read_u32(dev->of_node, + "qcom,tune2-efuse-correction", + &qphy->tune2_efuse_correction); if (ret) { dev_err(dev, "DT Value for tune2 efuse is invalid.\n"); diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index 6ba742076baa..2bc3c6fa417a 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -149,15 +149,17 @@ static void msm_ssusb_qmp_enable_autonomous(struct msm_ssphy_qmp *phy, if (enable) { msm_ssusb_qmp_clr_lfps_rxterm_int(phy); + val = readb_relaxed(phy->base + autonomous_mode_offset); + val |= ARCVR_DTCT_EN; if (phy->phy.flags & DEVICE_IN_SS_MODE) { - val = - readb_relaxed(phy->base + autonomous_mode_offset); - val |= ARCVR_DTCT_EN; val |= ALFPS_DTCT_EN; val &= ~ARCVR_DTCT_EVENT_SEL; - writeb_relaxed(val, phy->base + autonomous_mode_offset); + } else { + val &= ~ALFPS_DTCT_EN; + val |= ARCVR_DTCT_EVENT_SEL; } + writeb_relaxed(val, phy->base + autonomous_mode_offset); /* clamp phy level shifter to perform autonomous detection */ writel_relaxed(0x1, phy->vls_clamp_reg); } else { diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index d82fa36c3465..005da0866836 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -733,8 +733,10 @@ static int usbhsc_resume(struct device *dev) struct usbhs_priv *priv = dev_get_drvdata(dev); struct platform_device *pdev = usbhs_priv_to_pdev(priv); - if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) { usbhsc_power_ctrl(priv, 1); + usbhs_mod_autonomy_mode(priv); + } usbhs_platform_call(priv, phy_reset, pdev); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index efc4fae123a4..8647d2c2a8c4 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -37,6 +37,7 @@ struct usbhsg_gpriv; struct usbhsg_uep { struct usb_ep ep; struct usbhs_pipe *pipe; + spinlock_t lock; /* protect the pipe */ char ep_name[EP_NAME_SIZE]; @@ -638,10 +639,16 @@ usbhsg_ep_enable_end: static int usbhsg_ep_disable(struct usb_ep *ep) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); - struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usbhs_pipe *pipe; + unsigned long flags; + int ret = 0; - if (!pipe) - return -EINVAL; + spin_lock_irqsave(&uep->lock, flags); + pipe = usbhsg_uep_to_pipe(uep); + if (!pipe) { + ret = -EINVAL; + goto out; + } usbhsg_pipe_disable(uep); usbhs_pipe_free(pipe); @@ -649,6 +656,9 @@ static int usbhsg_ep_disable(struct usb_ep *ep) uep->pipe->mod_private = NULL; uep->pipe = NULL; +out: + spin_unlock_irqrestore(&uep->lock, flags); + return 0; } @@ -698,8 +708,11 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); - struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usbhs_pipe *pipe; + unsigned long flags; + spin_lock_irqsave(&uep->lock, flags); + pipe = usbhsg_uep_to_pipe(uep); if (pipe) usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq)); @@ -708,6 +721,7 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) * even if the pipe is NULL. */ usbhsg_queue_pop(uep, ureq, -ECONNRESET); + spin_unlock_irqrestore(&uep->lock, flags); return 0; } @@ -854,10 +868,10 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) { struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); - struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); + struct usbhsg_uep *uep; struct device *dev = usbhs_priv_to_dev(priv); unsigned long flags; - int ret = 0; + int ret = 0, i; /******************** spin lock ********************/ usbhs_lock(priv, flags); @@ -889,7 +903,9 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) usbhs_sys_set_test_mode(priv, 0); usbhs_sys_function_ctrl(priv, 0); - usbhsg_ep_disable(&dcp->ep); + /* disable all eps */ + usbhsg_for_each_uep_with_dcp(uep, gpriv, i) + usbhsg_ep_disable(&uep->ep); dev_dbg(dev, "stop gadget\n"); @@ -1072,6 +1088,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) ret = -ENOMEM; goto usbhs_mod_gadget_probe_err_gpriv; } + spin_lock_init(&uep->lock); gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED); dev_info(dev, "%stransceiver found\n", diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 33cec50978b8..b0dc6da3d970 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -134,6 +134,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */ { USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */ { USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */ + { USB_DEVICE(0x10C4, 0x8A5E) }, /* CEL EM3588 ZigBee USB Stick Long Range */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 3bf61acfc26b..ebe51f11105d 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1877,6 +1877,10 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&four_g_w100_blacklist }, { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) }, + { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9801, 0xff), + .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9803, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) }, { USB_DEVICE(HAIER_VENDOR_ID, HAIER_PRODUCT_CE100) }, diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index fd509ed6cf70..652b4334b26d 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -158,6 +158,7 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x1199, 0x9056)}, /* Sierra Wireless Modem */ {DEVICE_SWI(0x1199, 0x9060)}, /* Sierra Wireless Modem */ {DEVICE_SWI(0x1199, 0x9061)}, /* Sierra Wireless Modem */ + {DEVICE_SWI(0x1199, 0x9063)}, /* Sierra Wireless EM7305 */ {DEVICE_SWI(0x1199, 0x9070)}, /* Sierra Wireless MC74xx */ {DEVICE_SWI(0x1199, 0x9071)}, /* Sierra Wireless MC74xx */ {DEVICE_SWI(0x1199, 0x9078)}, /* Sierra Wireless EM74xx */ diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 39afd7045c43..7bb5f8da5357 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1520,8 +1520,11 @@ static void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us) /* Make sure driver was initialized */ - if (us->extra == NULL) + if (us->extra == NULL) { usb_stor_dbg(us, "ERROR Driver not initialized\n"); + srb->result = DID_ERROR << 16; + return; + } scsi_set_resid(srb, 0); /* scsi_bufflen might change in protocol translation to ata */ diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c index 44ab43fc4fcc..af10f7b131a4 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -262,7 +262,11 @@ void stub_device_cleanup_urbs(struct stub_device *sdev) kmem_cache_free(stub_priv_cache, priv); kfree(urb->transfer_buffer); + urb->transfer_buffer = NULL; + kfree(urb->setup_packet); + urb->setup_packet = NULL; + usb_free_urb(urb); } } diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index dbcabc9dbe0d..021003c4de53 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -28,7 +28,11 @@ static void stub_free_priv_and_urb(struct stub_priv *priv) struct urb *urb = priv->urb; kfree(urb->setup_packet); + urb->setup_packet = NULL; + kfree(urb->transfer_buffer); + urb->transfer_buffer = NULL; + list_del(&priv->list); kmem_cache_free(stub_priv_cache, priv); usb_free_urb(urb); diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 830e2fd47642..b31b84f56e8f 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -902,6 +902,10 @@ static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) return ret; vdev->barmap[index] = pci_iomap(pdev, index, 0); + if (!vdev->barmap[index]) { + pci_release_selected_regions(pdev, 1 << index); + return -ENOMEM; + } } vma->vm_private_data = vdev; diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index 210db24d2204..4d39f7959adf 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -190,7 +190,10 @@ ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, if (!vdev->has_vga) return -EINVAL; - switch (pos) { + if (pos > 0xbfffful) + return -EINVAL; + + switch ((u32)pos) { case 0xa0000 ... 0xbffff: count = min(count, (size_t)(0xc0000 - pos)); iomem = ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1); diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 6070b793cbcb..1e01e28f40f3 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -296,6 +296,34 @@ static void vfio_group_put(struct vfio_group *group) kref_put_mutex(&group->kref, vfio_group_release, &vfio.group_lock); } +struct vfio_group_put_work { + struct work_struct work; + struct vfio_group *group; +}; + +static void vfio_group_put_bg(struct work_struct *work) +{ + struct vfio_group_put_work *do_work; + + do_work = container_of(work, struct vfio_group_put_work, work); + + vfio_group_put(do_work->group); + kfree(do_work); +} + +static void vfio_group_schedule_put(struct vfio_group *group) +{ + struct vfio_group_put_work *do_work; + + do_work = kmalloc(sizeof(*do_work), GFP_KERNEL); + if (WARN_ON(!do_work)) + return; + + INIT_WORK(&do_work->work, vfio_group_put_bg); + do_work->group = group; + schedule_work(&do_work->work); +} + /* Assume group_lock or group reference is held */ static void vfio_group_get(struct vfio_group *group) { @@ -620,7 +648,14 @@ static int vfio_iommu_group_notifier(struct notifier_block *nb, break; } - vfio_group_put(group); + /* + * If we're the last reference to the group, the group will be + * released, which includes unregistering the iommu group notifier. + * We hold a read-lock on that notifier list, unregistering needs + * a write-lock... deadlock. Release our reference asynchronously + * to avoid that situation. + */ + vfio_group_schedule_put(group); return NOTIFY_OK; } @@ -1552,6 +1587,15 @@ void vfio_group_put_external_user(struct vfio_group *group) } EXPORT_SYMBOL_GPL(vfio_group_put_external_user); +bool vfio_external_group_match_file(struct vfio_group *test_group, + struct file *filep) +{ + struct vfio_group *group = filep->private_data; + + return (filep->f_op == &vfio_group_fops) && (group == test_group); +} +EXPORT_SYMBOL_GPL(vfio_external_group_match_file); + int vfio_external_user_iommu_id(struct vfio_group *group) { return iommu_group_id(group->iommu_group); diff --git a/drivers/video/fbdev/cobalt_lcdfb.c b/drivers/video/fbdev/cobalt_lcdfb.c index 07675d6f323e..d4530b54479c 100644 --- a/drivers/video/fbdev/cobalt_lcdfb.c +++ b/drivers/video/fbdev/cobalt_lcdfb.c @@ -350,6 +350,11 @@ static int cobalt_lcdfb_probe(struct platform_device *dev) info->screen_size = resource_size(res); info->screen_base = devm_ioremap(&dev->dev, res->start, info->screen_size); + if (!info->screen_base) { + framebuffer_release(info); + return -ENOMEM; + } + info->fbops = &cobalt_lcd_fbops; info->fix = cobalt_lcdfb_fix; info->fix.smem_start = res->start; diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c index c6ff92ed1686..3330f8f62b78 100644 --- a/drivers/video/fbdev/msm/mdss_dba_utils.c +++ b/drivers/video/fbdev/msm/mdss_dba_utils.c @@ -576,6 +576,7 @@ int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo) video_cfg.h_pulse_width = pinfo->lcdc.h_pulse_width; video_cfg.v_pulse_width = pinfo->lcdc.v_pulse_width; video_cfg.pclk_khz = (unsigned long)pinfo->clk_rate / 1000; + video_cfg.hdmi_mode = !hdmi_edid_is_dvi_mode(ud->edid_data); /* Calculate number of DSI lanes configured */ video_cfg.num_of_input_lanes = 0; @@ -591,8 +592,6 @@ int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo) /* Get scan information from EDID */ video_cfg.vic = mdss_dba_get_vic_panel_info(ud, pinfo); ud->current_vic = video_cfg.vic; - video_cfg.hdmi_mode = hdmi_edid_get_sink_mode(ud->edid_data, - video_cfg.vic); video_cfg.scaninfo = hdmi_edid_get_sink_scaninfo(ud->edid_data, video_cfg.vic); if (ud->ops.video_on) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 2f169074261a..be4d1ec1589b 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1094,8 +1094,14 @@ static int dp_audio_info_setup(struct platform_device *pdev, struct mdss_dp_drv_pdata *dp_ctrl = platform_get_drvdata(pdev); if (!dp_ctrl || !params) { - DEV_ERR("%s: invalid input\n", __func__); - return -ENODEV; + pr_err("invalid input\n"); + rc = -ENODEV; + goto end; + } + + if (!dp_ctrl->power_on) { + pr_debug("DP is already power off\n"); + goto end; } mdss_dp_audio_setup_sdps(&dp_ctrl->ctrl_io, params->num_of_channels); @@ -1103,6 +1109,7 @@ static int dp_audio_info_setup(struct platform_device *pdev, mdss_dp_set_safe_to_exit_level(&dp_ctrl->ctrl_io, dp_ctrl->lane_cnt); mdss_dp_audio_enable(&dp_ctrl->ctrl_io, true); +end: return rc; } /* dp_audio_info_setup */ @@ -1134,6 +1141,11 @@ static void dp_audio_teardown_done(struct platform_device *pdev) return; } + if (!dp->power_on) { + pr_err("DP is already power off\n"); + return; + } + mdss_dp_audio_enable(&dp->ctrl_io, false); /* Make sure the DP audio engine is disabled */ wmb(); @@ -1625,6 +1637,7 @@ int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv) dp_drv->link_rate = mdss_dp_gen_link_clk(dp_drv); if (!dp_drv->link_rate) { pr_err("Unable to configure required link rate\n"); + mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); ret = -EINVAL; goto exit; } @@ -3982,12 +3995,6 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) { int ret = 0; - /* In case of HPD_IRQ events without DP link being turned on such as - * adb shell stop, skip handling hpd_irq event. - */ - if (!dp->dp_initialized) - goto exit; - pr_debug("start\n"); dp->hpd_irq_on = true; @@ -4103,6 +4110,15 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv) if (dp_drv->alt_mode.dp_status.hpd_irq) { pr_debug("Attention: hpd_irq high\n"); + /* In case of HPD_IRQ events without DP link being + * turned on such as adb shell stop, skip handling + * hpd_irq event. + */ + if (!dp_drv->dp_initialized) { + pr_err("DP not initialized yet\n"); + return; + } + if (dp_is_hdcp_enabled(dp_drv) && dp_drv->hdcp.ops->cp_irq) { if (!dp_drv->hdcp.ops->cp_irq(dp_drv->hdcp.data)) return; diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 23a63121b78c..c0632e8241a0 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -395,6 +395,7 @@ static int dp_aux_rw_cmds_retry(struct mdss_dp_drv_pdata *dp, int i; u32 aux_cfg1_config_count; int ret; + bool connected = false; aux_cfg1_config_count = mdss_dp_phy_aux_get_config_cnt(dp, PHY_AUX_CFG1); @@ -404,6 +405,15 @@ retry: do { struct edp_cmd cmd1 = *cmd; + mutex_lock(&dp->attention_lock); + connected = dp->cable_connected; + mutex_unlock(&dp->attention_lock); + + if (!connected) { + pr_err("dp cable disconnected\n"); + break; + } + dp->aux_error_num = EDP_AUX_ERR_NONE; pr_debug("Trying %s, iteration count: %d\n", mdss_dp_aux_transaction_to_string(transaction), @@ -674,6 +684,11 @@ char mdss_dp_gen_link_clk(struct mdss_dp_drv_pdata *dp) pr_debug("clk_rate=%llu, bpp= %d, lane_cnt=%d\n", pinfo->clk_rate, pinfo->bpp, lane_cnt); + if (lane_cnt == 0) { + pr_warn("Invalid max lane count\n"); + return 0; + } + /* * The max pixel clock supported is 675Mhz. The * current calculations below will make sure diff --git a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c index 5a677dfe7484..36a062dc5207 100644 --- a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c @@ -894,22 +894,24 @@ static bool dp_hdcp2p2_supported(struct dp_hdcp2p2_ctrl *ctrl) { struct edp_cmd cmd = {0}; const u32 offset = 0x6921d; - u8 buf; + u8 buf[3]; cmd.read = 1; cmd.addr = offset; - cmd.len = sizeof(buf); - cmd.out_buf = &buf; + cmd.len = ARRAY_SIZE(buf); + cmd.out_buf = buf; if (dp_aux_read(ctrl->init_data.cb_data, &cmd)) { pr_err("RxCaps read failed\n"); goto error; } - pr_debug("rxcaps 0x%x\n", buf); + pr_debug("HDCP_CAPABLE=%lu\n", (buf[2] & BIT(1)) >> 1); + pr_debug("VERSION=%d\n", buf[0]); - if (buf & BIT(1)) + if ((buf[2] & BIT(1)) && (buf[0] == 0x2)) return true; + error: return false; } diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 4ac10ab494e5..5f7e7c6bcde0 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -2720,8 +2720,6 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, ctrl_pdata->update_phy_timing); rc = mdss_dsi_on(pdata); - mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode, - pdata); break; case MDSS_EVENT_UNBLANK: if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE) @@ -3268,21 +3266,6 @@ end: return rc; } -static void mdss_dsi_ctrl_validate_lane_swap_config( - struct mdss_dsi_ctrl_pdata *ctrl) -{ - struct mipi_panel_info *mipi = &ctrl->panel_data.panel_info.mipi; - - if (!mipi->data_lane0) - ctrl->lane_map[DSI_LOGICAL_LANE_0] = DSI_PHYSICAL_LANE_INVALID; - if (!mipi->data_lane1) - ctrl->lane_map[DSI_LOGICAL_LANE_1] = DSI_PHYSICAL_LANE_INVALID; - if (!mipi->data_lane2) - ctrl->lane_map[DSI_LOGICAL_LANE_2] = DSI_PHYSICAL_LANE_INVALID; - if (!mipi->data_lane3) - ctrl->lane_map[DSI_LOGICAL_LANE_3] = DSI_PHYSICAL_LANE_INVALID; -} - static int mdss_dsi_ctrl_validate_config(struct mdss_dsi_ctrl_pdata *ctrl) { int rc = 0; @@ -3292,8 +3275,6 @@ static int mdss_dsi_ctrl_validate_config(struct mdss_dsi_ctrl_pdata *ctrl) goto error; } - mdss_dsi_ctrl_validate_lane_swap_config(ctrl); - /* * check to make sure that the byte interface clock is specified for * DSI ctrl version 2 and above. diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index a953dd1a2ac2..2143c2bdb84b 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -62,6 +62,11 @@ #define EDID_VENDOR_ID_SIZE 4 #define EDID_IEEE_REG_ID 0x0c03 +enum edid_sink_mode { + SINK_MODE_DVI, + SINK_MODE_HDMI +}; + enum luminance_value { NO_LUMINANCE_DATA = 3, MAXIMUM_LUMINANCE = 4, @@ -151,10 +156,13 @@ struct hdmi_edid_ctrl { }; static bool hdmi_edid_is_mode_supported(struct hdmi_edid_ctrl *edid_ctrl, - struct msm_hdmi_mode_timing_info *timing) + struct msm_hdmi_mode_timing_info *timing, u32 out_format) { + u32 pclk = hdmi_tx_setup_tmds_clk_rate(timing->pixel_freq, + out_format, false); + if (!timing->supported || - timing->pixel_freq > edid_ctrl->init_data.max_pclk_khz) + pclk > edid_ctrl->init_data.max_pclk_khz) return false; return true; @@ -931,7 +939,8 @@ static void hdmi_edid_add_sink_y420_format(struct hdmi_edid_ctrl *edid_ctrl, u32 ret = hdmi_get_supported_mode(&timing, &edid_ctrl->init_data.ds_data, video_format); - u32 supported = hdmi_edid_is_mode_supported(edid_ctrl, &timing); + u32 supported = hdmi_edid_is_mode_supported(edid_ctrl, + &timing, MDP_Y_CBCR_H2V2); struct hdmi_edid_sink_data *sink = &edid_ctrl->sink_data; if (video_format >= HDMI_VFRMT_MAX) { @@ -1699,7 +1708,8 @@ static void hdmi_edid_add_sink_video_format(struct hdmi_edid_ctrl *edid_ctrl, u32 ret = hdmi_get_supported_mode(&timing, &edid_ctrl->init_data.ds_data, video_format); - u32 supported = hdmi_edid_is_mode_supported(edid_ctrl, &timing); + u32 supported = hdmi_edid_is_mode_supported(edid_ctrl, + &timing, MDP_RGBA_8888); struct hdmi_edid_sink_data *sink_data = &edid_ctrl->sink_data; struct disp_mode_info *disp_mode_list = sink_data->disp_mode_list; u32 i = 0; @@ -2418,7 +2428,7 @@ end: return scaninfo; } /* hdmi_edid_get_sink_scaninfo */ -u32 hdmi_edid_get_sink_mode(void *input, u32 mode) +static u32 hdmi_edid_get_sink_mode(void *input) { struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; bool sink_mode; @@ -2431,13 +2441,8 @@ u32 hdmi_edid_get_sink_mode(void *input, u32 mode) if (edid_ctrl->edid_override && (edid_ctrl->override_data.sink_mode != -1)) sink_mode = edid_ctrl->override_data.sink_mode; - else { - if (edid_ctrl->sink_mode && - (mode > 0 && mode <= HDMI_EVFRMT_END)) - sink_mode = SINK_MODE_HDMI; - else - sink_mode = SINK_MODE_DVI; - } + else + sink_mode = edid_ctrl->sink_mode; return sink_mode; } /* hdmi_edid_get_sink_mode */ @@ -2452,21 +2457,10 @@ u32 hdmi_edid_get_sink_mode(void *input, u32 mode) */ bool hdmi_edid_is_dvi_mode(void *input) { - struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; - int sink_mode; - - if (!edid_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return true; - } - - if (edid_ctrl->edid_override && - (edid_ctrl->override_data.sink_mode != -1)) - sink_mode = edid_ctrl->override_data.sink_mode; + if (hdmi_edid_get_sink_mode(input)) + return false; else - sink_mode = edid_ctrl->sink_mode; - - return (sink_mode == SINK_MODE_DVI); + return true; } /** diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index c604d0fbf7b2..af802bb45f89 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -58,16 +58,10 @@ struct hdmi_edid_override_data { int vic; }; -enum edid_sink_mode { - SINK_MODE_DVI, - SINK_MODE_HDMI -}; - int hdmi_edid_parser(void *edid_ctrl); u32 hdmi_edid_get_raw_data(void *edid_ctrl, u8 *buf, u32 size); u8 hdmi_edid_get_sink_scaninfo(void *edid_ctrl, u32 resolution); bool hdmi_edid_is_dvi_mode(void *input); -u32 hdmi_edid_get_sink_mode(void *edid_ctrl, u32 mode); bool hdmi_edid_sink_scramble_override(void *input); bool hdmi_edid_get_sink_scrambler_support(void *input); bool hdmi_edid_get_scdc_support(void *input); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 2e267f2695d7..837147dc5036 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -272,20 +272,6 @@ static void hdmi_tx_cable_notify_work(struct work_struct *work) mutex_unlock(&hdmi_ctrl->tx_lock); } /* hdmi_tx_cable_notify_work */ -static bool hdmi_tx_is_cea_format(int mode) -{ - bool cea_fmt; - - if ((mode > 0) && (mode <= HDMI_EVFRMT_END)) - cea_fmt = true; - else - cea_fmt = false; - - DEV_DBG("%s: %s\n", __func__, cea_fmt ? "Yes" : "No"); - - return cea_fmt; -} - static inline bool hdmi_tx_is_hdcp_enabled(struct hdmi_tx_ctrl *hdmi_ctrl) { return hdmi_ctrl->hdcp_feature_on && @@ -375,12 +361,11 @@ static void hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl) } } -static inline bool hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl) +static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl) { void *data = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID); - return (hdmi_edid_get_sink_mode(data, - hdmi_ctrl->vic) == SINK_MODE_DVI); + return hdmi_edid_is_dvi_mode(data); } /* hdmi_tx_is_dvi_mode */ static inline u32 hdmi_tx_is_in_splash(struct hdmi_tx_ctrl *hdmi_ctrl) @@ -2194,6 +2179,7 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl) int status = 0; void *data; struct dss_io_data *io; + u32 sink_max_pclk; if (!hdmi_ctrl) { DEV_ERR("%s: invalid input\n", __func__); @@ -2233,16 +2219,22 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl) /* parse edid if a valid edid buffer is present */ if (hdmi_ctrl->custom_edid || !hdmi_ctrl->sim_mode) { status = hdmi_edid_parser(data); - if (status) + if (status) { DEV_ERR("%s: edid parse failed\n", __func__); - else + } else { /* - * Updata HDMI max supported TMDS clock, consider - * both sink and source capicity. + * Update HDMI max supported TMDS clock, consider + * both sink and source capacity. For DVI sink, + * could not get max TMDS clock from EDID, so just + * use source capacity. */ - hdmi_edid_set_max_pclk_rate(data, - min(hdmi_edid_get_sink_caps_max_tmds_clk(data) / 1000, - hdmi_ctrl->max_pclk_khz)); + sink_max_pclk = + hdmi_edid_get_sink_caps_max_tmds_clk(data); + if (sink_max_pclk != 0) + hdmi_edid_set_max_pclk_rate(data, + min(sink_max_pclk / 1000, + hdmi_ctrl->max_pclk_khz)); + } } bail: if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, false)) @@ -2482,8 +2474,7 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) hdmi_ctrl_reg |= BIT(2); /* Set transmission mode to DVI based in EDID info */ - if (hdmi_edid_get_sink_mode(data, - hdmi_ctrl->vic) == SINK_MODE_DVI) + if (hdmi_edid_is_dvi_mode(data)) hdmi_ctrl_reg &= ~BIT(1); /* DVI mode */ /* @@ -2942,6 +2933,7 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, { int rc = 0; struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev); + u32 is_mode_dvi; if (!hdmi_ctrl || !params) { DEV_ERR("%s: invalid input\n", __func__); @@ -2950,8 +2942,9 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, mutex_lock(&hdmi_ctrl->tx_lock); - if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) && - hdmi_tx_is_panel_on(hdmi_ctrl)) { + is_mode_dvi = hdmi_tx_is_dvi_mode(hdmi_ctrl); + + if (!is_mode_dvi && hdmi_tx_is_panel_on(hdmi_ctrl)) { memcpy(&hdmi_ctrl->audio_params, params, sizeof(struct msm_ext_disp_audio_setup_params)); @@ -3192,9 +3185,7 @@ static int hdmi_tx_power_on(struct hdmi_tx_ctrl *hdmi_ctrl) } hdmi_ctrl->panel.vic = hdmi_ctrl->vic; - - if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) && - hdmi_tx_is_cea_format(hdmi_ctrl->vic)) + if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) hdmi_ctrl->panel.infoframe = true; else hdmi_ctrl->panel.infoframe = false; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 6fb32761a767..410d36a3ac31 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1797,7 +1797,8 @@ static inline int mdss_mdp_irq_clk_register(struct mdss_data_type *mdata, static void __mdss_restore_sec_cfg(struct mdss_data_type *mdata) { - int ret, scm_ret = 0; + int ret; + u64 scm_ret = 0; if (test_bit(MDSS_CAPS_SCM_RESTORE_NOT_REQUIRED, mdata->mdss_caps_map)) return; @@ -1808,7 +1809,7 @@ static void __mdss_restore_sec_cfg(struct mdss_data_type *mdata) ret = scm_restore_sec_cfg(SEC_DEVICE_MDSS, 0, &scm_ret); if (ret || scm_ret) - pr_warn("scm_restore_sec_cfg failed %d %d\n", + pr_warn("scm_restore_sec_cfg failed %d %llu\n", ret, scm_ret); __mdss_mdp_reg_access_clk_enable(mdata, false); @@ -2779,6 +2780,8 @@ ssize_t mdss_mdp_show_capabilities(struct device *dev, SPRINT(" avr"); if (mdss_has_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED)) SPRINT(" hdr"); + if (mdata->nvig_pipes && mdata->mdp_rev >= MDSS_MDP_HW_REV_300) + SPRINT(" vig_csc_db"); /* double buffered VIG CSC block */ SPRINT("\n"); #undef SPRINT diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 0165c48a0467..54fb21a5f35d 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -696,7 +696,6 @@ static u32 get_pipe_mdp_clk_rate(struct mdss_mdp_pipe *pipe, if (flags & PERF_CALC_PIPE_APPLY_CLK_FUDGE) rate = mdss_mdp_clk_fudge_factor(mixer, rate); - rate = min(mdata->max_mdp_clk_rate, rate); return rate; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c index 1035d23fe9ce..09d1dab0d180 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_debug.c +++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c @@ -1863,12 +1863,14 @@ static void __print_buf(struct seq_file *s, struct mdss_mdp_data *buf, seq_puts(s, "\n"); } -static void __dump_pipe(struct seq_file *s, struct mdss_mdp_pipe *pipe) +static void __dump_pipe(struct seq_file *s, struct mdss_mdp_pipe *pipe, + struct msm_fb_data_type *mfd) { struct mdss_mdp_data *buf; int format; int smps[4]; int i; + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); seq_printf(s, "\nSSPP #%d type=%s ndx=%x flags=0x%16llx play_cnt=%u xin_id=%d\n", pipe->num, mdss_mdp_pipetype2str(pipe->type), @@ -1923,11 +1925,14 @@ static void __dump_pipe(struct seq_file *s, struct mdss_mdp_pipe *pipe) seq_puts(s, "Data:\n"); + mutex_lock(&mdp5_data->list_lock); list_for_each_entry(buf, &pipe->buf_queue, pipe_list) __print_buf(s, buf, false); + mutex_unlock(&mdp5_data->list_lock); } -static void __dump_mixer(struct seq_file *s, struct mdss_mdp_mixer *mixer) +static void __dump_mixer(struct seq_file *s, struct mdss_mdp_mixer *mixer, + struct msm_fb_data_type *mfd) { struct mdss_mdp_pipe *pipe; int i, cnt = 0; @@ -1944,7 +1949,7 @@ static void __dump_mixer(struct seq_file *s, struct mdss_mdp_mixer *mixer) for (i = 0; i < ARRAY_SIZE(mixer->stage_pipe); i++) { pipe = mixer->stage_pipe[i]; if (pipe) { - __dump_pipe(s, pipe); + __dump_pipe(s, pipe, mfd); cnt++; } } @@ -2019,8 +2024,8 @@ static void __dump_ctl(struct seq_file *s, struct mdss_mdp_ctl *ctl) seq_printf(s, "Play Count=%u Underrun Count=%u\n", ctl->play_cnt, ctl->underrun_cnt); - __dump_mixer(s, ctl->mixer_left); - __dump_mixer(s, ctl->mixer_right); + __dump_mixer(s, ctl->mixer_left, ctl->mfd); + __dump_mixer(s, ctl->mixer_right, ctl->mfd); } static int __dump_mdp(struct seq_file *s, struct mdss_data_type *mdata) diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index c31c0149866a..bdf6705ef597 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -643,8 +643,13 @@ static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl, display_hctl = (hsync_end_x << 16) | hsync_start_x; den_polarity = 0; - hsync_polarity = p->h_polarity; - vsync_polarity = p->v_polarity; + if (ctx->intf_type == MDSS_INTF_HDMI) { + hsync_polarity = p->h_polarity; + vsync_polarity = p->v_polarity; + } else { + hsync_polarity = 0; + vsync_polarity = 0; + } polarity_ctl = (den_polarity << 2) | /* DEN Polarity */ (vsync_polarity << 1) | /* VSYNC Polarity */ (hsync_polarity << 0); /* HSYNC Polarity */ diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 3ccc09d58480..ff93c343d41f 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -975,26 +975,31 @@ static int __validate_layer_reconfig(struct mdp_input_layer *layer, struct mdss_mdp_pipe *pipe) { int status = 0; - struct mdss_mdp_format_params *src_fmt; + struct mdss_mdp_format_params *layer_src_fmt; + struct mdss_data_type *mdata = mfd_to_mdata(pipe->mfd); + bool is_csc_db = (mdata->mdp_rev < MDSS_MDP_HW_REV_300) ? false : true; + + layer_src_fmt = mdss_mdp_get_format_params(layer->buffer.format); + if (!layer_src_fmt) { + pr_err("Invalid layer format %d\n", layer->buffer.format); + status = -EINVAL; + goto err_exit; + } /* - * csc registers are not double buffered. It is not permitted - * to change them on staged pipe with YUV layer. + * HW earlier to sdm 3.x.x does not support double buffer CSC. + * Invalidate any reconfig of CSC block on staged pipe. */ - if (pipe->csc_coeff_set != layer->color_space) { - src_fmt = mdss_mdp_get_format_params(layer->buffer.format); - if (!src_fmt) { - pr_err("Invalid layer format %d\n", - layer->buffer.format); - status = -EINVAL; - } else { - if (pipe->src_fmt->is_yuv && src_fmt && - src_fmt->is_yuv) { - status = -EPERM; - pr_err("csc change is not permitted on used pipe\n"); - } - } + if (!is_csc_db && + ((!!pipe->src_fmt->is_yuv != !!layer_src_fmt->is_yuv) || + (pipe->src_fmt->is_yuv && layer_src_fmt->is_yuv && + pipe->csc_coeff_set != layer->color_space))) { + pr_err("CSC reconfig not allowed on staged pipe\n"); + status = -EINVAL; + goto err_exit; } + +err_exit: return status; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c b/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c index 71cab148e1c3..aabf7c507376 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -358,7 +358,8 @@ static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data, int ret = 0, i = 0; char __iomem *hist_addr; u32 sz = 0, temp = 0, *data = NULL; - struct mdp_hist_lut_data_v1_7 *lut_data = NULL; + struct mdp_hist_lut_data_v1_7 lut_data_v1_7; + struct mdp_hist_lut_data_v1_7 *lut_data = &lut_data_v1_7; struct mdp_hist_lut_data *lut_cfg_data = NULL; if (!base_addr || !cfg_data) { @@ -378,7 +379,11 @@ static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data, lut_cfg_data->version, lut_cfg_data->cfg_payload); return -EINVAL; } - lut_data = lut_cfg_data->cfg_payload; + if (copy_from_user(lut_data, (void __user *) lut_cfg_data->cfg_payload, + sizeof(*lut_data))) { + pr_err("copy from user failed for lut_data\n"); + return -EFAULT; + } if (lut_data->len != ENHIST_LUT_ENTRIES) { pr_err("invalid hist_lut len %d", lut_data->len); return -EINVAL; @@ -1786,7 +1791,8 @@ static int pp_igc_get_config(char __iomem *base_addr, void *cfg_data, { int ret = 0, i = 0; struct mdp_igc_lut_data *lut_cfg_data = NULL; - struct mdp_igc_lut_data_v1_7 *lut_data = NULL; + struct mdp_igc_lut_data_v1_7 lut_data_v1_7; + struct mdp_igc_lut_data_v1_7 *lut_data = &lut_data_v1_7; char __iomem *c1 = NULL, *c2 = NULL; u32 *c0c1_data = NULL, *c2_data = NULL; u32 data = 0, sz = 0; @@ -1810,7 +1816,11 @@ static int pp_igc_get_config(char __iomem *base_addr, void *cfg_data, ret = -EINVAL; goto exit; } - lut_data = lut_cfg_data->cfg_payload; + if (copy_from_user(lut_data, (void __user *) lut_cfg_data->cfg_payload, + sizeof(*lut_data))) { + pr_err("copy from user failed for lut_data\n"); + return -EFAULT; + } if (lut_data->len != IGC_LUT_ENTRIES) { pr_err("invalid lut len %d\n", lut_data->len); ret = -EINVAL; @@ -1954,20 +1964,24 @@ static int pp_pgc_get_config(char __iomem *base_addr, void *cfg_data, u32 *c0_data = NULL, *c1_data = NULL, *c2_data = NULL; u32 val = 0, i = 0, sz = 0; struct mdp_pgc_lut_data *pgc_data = NULL; - struct mdp_pgc_lut_data_v1_7 *pgc_data_v17 = NULL; + struct mdp_pgc_lut_data_v1_7 pgc_lut_data_v17; + struct mdp_pgc_lut_data_v1_7 *pgc_data_v17 = &pgc_lut_data_v17; if (!base_addr || !cfg_data) { pr_err("invalid params base_addr %pK cfg_data %pK block_type %d\n", base_addr, cfg_data, block_type); return -EINVAL; } pgc_data = (struct mdp_pgc_lut_data *) cfg_data; - pgc_data_v17 = (struct mdp_pgc_lut_data_v1_7 *) - pgc_data->cfg_payload; - if (pgc_data->version != mdp_pgc_v1_7 || !pgc_data_v17) { + if (pgc_data->version != mdp_pgc_v1_7 || !pgc_data->cfg_payload) { pr_err("invalid pgc version %d payload %pK\n", - pgc_data->version, pgc_data_v17); + pgc_data->version, pgc_data->cfg_payload); return -EINVAL; } + if (copy_from_user(pgc_data_v17, (void __user *) pgc_data->cfg_payload, + sizeof(*pgc_data_v17))) { + pr_err("copy from user failed for pgc lut data\n"); + return -EFAULT; + } if (!(pgc_data->flags & MDP_PP_OPS_READ)) { pr_info("read ops is not set %d", pgc_data->flags); return -EINVAL; diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c b/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c index 25cb94f89dd5..b377f0921508 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -298,7 +298,8 @@ static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data, int ret = 0, i = 0; char __iomem *hist_lut_addr; u32 sz = 0, temp = 0, *data = NULL; - struct mdp_hist_lut_data_v1_7 *lut_data = NULL; + struct mdp_hist_lut_data_v1_7 lut_data_v1_7; + struct mdp_hist_lut_data_v1_7 *lut_data = &lut_data_v1_7; struct mdp_hist_lut_data *lut_cfg_data = NULL; if (!base_addr || !cfg_data) { @@ -323,7 +324,11 @@ static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data, lut_cfg_data->version, lut_cfg_data->cfg_payload); return -EINVAL; } - lut_data = lut_cfg_data->cfg_payload; + if (copy_from_user(lut_data, (void __user *) lut_cfg_data->cfg_payload, + sizeof(*lut_data))) { + pr_err("copy from user failed for lut_data\n"); + return -EFAULT; + } if (lut_data->len != ENHIST_LUT_ENTRIES) { pr_err("invalid hist_lut len %d", lut_data->len); return -EINVAL; diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c index 61b0518d3ee6..2028222748c3 100644 --- a/drivers/video/fbdev/msm/mdss_rotator.c +++ b/drivers/video/fbdev/msm/mdss_rotator.c @@ -373,6 +373,15 @@ static bool mdss_rotator_is_work_pending(struct mdss_rot_mgr *mgr, return false; } +static void mdss_rotator_install_fence_fd(struct mdss_rot_entry_container *req) +{ + int i = 0; + + for (i = 0; i < req->count; i++) + sync_fence_install(req->entries[i].output_fence, + req->entries[i].output_fence_fd); +} + static int mdss_rotator_create_fence(struct mdss_rot_entry *entry) { int ret = 0, fd; @@ -411,7 +420,6 @@ static int mdss_rotator_create_fence(struct mdss_rot_entry *entry) goto get_fd_err; } - sync_fence_install(fence, fd); rot_timeline->next_value++; mutex_unlock(&rot_timeline->lock); @@ -2240,6 +2248,7 @@ static int mdss_rotator_handle_request(struct mdss_rot_mgr *mgr, goto handle_request_err1; } + mdss_rotator_install_fence_fd(req); mdss_rotator_queue_request(mgr, private, req); mutex_unlock(&mgr->lock); @@ -2400,6 +2409,7 @@ static int mdss_rotator_handle_request32(struct mdss_rot_mgr *mgr, goto handle_request32_err1; } + mdss_rotator_install_fence_fd(req); mdss_rotator_queue_request(mgr, private, req); mutex_unlock(&mgr->lock); diff --git a/fs/Kconfig b/fs/Kconfig index a5d2dc39ba07..4adb93ec85ea 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -282,4 +282,9 @@ endif # NETWORK_FILESYSTEMS source "fs/nls/Kconfig" source "fs/dlm/Kconfig" +config FILE_TABLE_DEBUG + bool "Enable FILE_TABLE_DEBUG" + help + This option enables debug of the open files using a global filetable + endmenu diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 6c031dd1bc4e..8a0243efd359 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -905,17 +905,60 @@ static int load_elf_binary(struct linux_binprm *bprm) elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE; vaddr = elf_ppnt->p_vaddr; + /* + * If we are loading ET_EXEC or we have already performed + * the ET_DYN load_addr calculations, proceed normally. + */ if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) { elf_flags |= MAP_FIXED; } else if (loc->elf_ex.e_type == ET_DYN) { - /* Try and get dynamic programs out of the way of the - * default mmap base, as well as whatever program they - * might try to exec. This is because the brk will - * follow the loader, and is not movable. */ - load_bias = ELF_ET_DYN_BASE - vaddr; - if (current->flags & PF_RANDOMIZE) - load_bias += arch_mmap_rnd(); - load_bias = ELF_PAGESTART(load_bias); + /* + * This logic is run once for the first LOAD Program + * Header for ET_DYN binaries to calculate the + * randomization (load_bias) for all the LOAD + * Program Headers, and to calculate the entire + * size of the ELF mapping (total_size). (Note that + * load_addr_set is set to true later once the + * initial mapping is performed.) + * + * There are effectively two types of ET_DYN + * binaries: programs (i.e. PIE: ET_DYN with INTERP) + * and loaders (ET_DYN without INTERP, since they + * _are_ the ELF interpreter). The loaders must + * be loaded away from programs since the program + * may otherwise collide with the loader (especially + * for ET_EXEC which does not have a randomized + * position). For example to handle invocations of + * "./ld.so someprog" to test out a new version of + * the loader, the subsequent program that the + * loader loads must avoid the loader itself, so + * they cannot share the same load range. Sufficient + * room for the brk must be allocated with the + * loader as well, since brk must be available with + * the loader. + * + * Therefore, programs are loaded offset from + * ELF_ET_DYN_BASE and loaders are loaded into the + * independently randomized mmap region (0 load_bias + * without MAP_FIXED). + */ + if (elf_interpreter) { + load_bias = ELF_ET_DYN_BASE; + if (current->flags & PF_RANDOMIZE) + load_bias += arch_mmap_rnd(); + elf_flags |= MAP_FIXED; + } else + load_bias = 0; + + /* + * Since load_bias is used for all subsequent loading + * calculations, we must lower it by the first vaddr + * so that the remaining calculations based on the + * ELF vaddrs will be correctly offset. The result + * is then page aligned. + */ + load_bias = ELF_PAGESTART(load_bias - vaddr); + total_size = total_mapping_size(elf_phdata, loc->elf_ex.e_phnum); if (!total_size) { diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a61926cb01c0..bebd6517355d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7521,11 +7521,18 @@ static void adjust_dio_outstanding_extents(struct inode *inode, * within our reservation, otherwise we need to adjust our inode * counter appropriately. */ - if (dio_data->outstanding_extents) { + if (dio_data->outstanding_extents >= num_extents) { dio_data->outstanding_extents -= num_extents; } else { + /* + * If dio write length has been split due to no large enough + * contiguous space, we need to compensate our inode counter + * appropriately. + */ + u64 num_needed = num_extents - dio_data->outstanding_extents; + spin_lock(&BTRFS_I(inode)->lock); - BTRFS_I(inode)->outstanding_extents += num_extents; + BTRFS_I(inode)->outstanding_extents += num_needed; spin_unlock(&BTRFS_I(inode)->lock); } } diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 9314b4ea2375..be7d187d53fd 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -247,6 +247,11 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx, if (ret < 0) err = ret; dput(last); + /* last_name no longer match cache index */ + if (fi->readdir_cache_idx >= 0) { + fi->readdir_cache_idx = -1; + fi->dir_release_count = 0; + } } return err; } diff --git a/fs/dcache.c b/fs/dcache.c index 4c07a84d1317..ba56a39a3b74 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -269,6 +269,33 @@ static inline int dname_external(const struct dentry *dentry) return dentry->d_name.name != dentry->d_iname; } +void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry) +{ + spin_lock(&dentry->d_lock); + if (unlikely(dname_external(dentry))) { + struct external_name *p = external_name(dentry); + atomic_inc(&p->u.count); + spin_unlock(&dentry->d_lock); + name->name = p->name; + } else { + memcpy(name->inline_name, dentry->d_iname, DNAME_INLINE_LEN); + spin_unlock(&dentry->d_lock); + name->name = name->inline_name; + } +} +EXPORT_SYMBOL(take_dentry_name_snapshot); + +void release_dentry_name_snapshot(struct name_snapshot *name) +{ + if (unlikely(name->name != name->inline_name)) { + struct external_name *p; + p = container_of(name->name, struct external_name, name[0]); + if (unlikely(atomic_dec_and_test(&p->u.count))) + kfree_rcu(p, u.head); + } +} +EXPORT_SYMBOL(release_dentry_name_snapshot); + static inline void __d_set_inode_and_type(struct dentry *dentry, struct inode *inode, unsigned type_flags) diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 0f5d05bf2131..e49ba072bd64 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -669,7 +669,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, { int error; struct dentry *dentry = NULL, *trap; - const char *old_name; + struct name_snapshot old_name; trap = lock_rename(new_dir, old_dir); /* Source or destination directories don't exist? */ @@ -684,19 +684,19 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry)) goto exit; - old_name = fsnotify_oldname_init(old_dentry->d_name.name); + take_dentry_name_snapshot(&old_name, old_dentry); error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir), dentry); if (error) { - fsnotify_oldname_free(old_name); + release_dentry_name_snapshot(&old_name); goto exit; } d_move(old_dentry, dentry); - fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name, + fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name, d_is_dir(old_dentry), NULL, old_dentry); - fsnotify_oldname_free(old_name); + release_dentry_name_snapshot(&old_name); unlock_rename(new_dir, old_dir); dput(dentry); return old_dentry; diff --git a/fs/exec.c b/fs/exec.c index 073ae12b396e..0428c34d4773 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -206,8 +206,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, if (write) { unsigned long size = bprm->vma->vm_end - bprm->vma->vm_start; - unsigned long ptr_size; - struct rlimit *rlim; + unsigned long ptr_size, limit; /* * Since the stack will hold pointers to the strings, we @@ -236,14 +235,16 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, return page; /* - * Limit to 1/4-th the stack size for the argv+env strings. + * Limit to 1/4 of the max stack size or 3/4 of _STK_LIM + * (whichever is smaller) for the argv+env strings. * This ensures that: * - the remaining binfmt code will not run out of stack space, * - the program will have a reasonable amount of stack left * to work from. */ - rlim = current->signal->rlim; - if (size > READ_ONCE(rlim[RLIMIT_STACK].rlim_cur) / 4) + limit = _STK_LIM / 4 * 3; + limit = min(limit, rlimit(RLIMIT_STACK) / 4); + if (size > limit) goto fail; } diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c index 5d09ea585840..c2ee23acf359 100644 --- a/fs/ext4/sysfs.c +++ b/fs/ext4/sysfs.c @@ -100,7 +100,7 @@ static ssize_t reserved_clusters_store(struct ext4_attr *a, int ret; ret = kstrtoull(skip_spaces(buf), 0, &val); - if (!ret || val >= clusters) + if (ret || val >= clusters) return -EINVAL; atomic64_set(&sbi->s_resv_clusters, val); diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index e9a8d676c6bc..83dcf7bfd7b8 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -213,7 +213,7 @@ static int __f2fs_set_acl(struct inode *inode, int type, switch (type) { case ACL_TYPE_ACCESS: name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; - if (acl) { + if (acl && !ipage) { error = posix_acl_update_mode(inode, &inode->i_mode, &acl); if (error) return error; diff --git a/fs/fcntl.c b/fs/fcntl.c index ee85cd4e136a..62376451bbce 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -740,16 +740,10 @@ static int __init fcntl_init(void) * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY * is defined as O_NONBLOCK on some platforms and not on others. */ - BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( - O_RDONLY | O_WRONLY | O_RDWR | - O_CREAT | O_EXCL | O_NOCTTY | - O_TRUNC | O_APPEND | /* O_NONBLOCK | */ - __O_SYNC | O_DSYNC | FASYNC | - O_DIRECT | O_LARGEFILE | O_DIRECTORY | - O_NOFOLLOW | O_NOATIME | O_CLOEXEC | - __FMODE_EXEC | O_PATH | __O_TMPFILE | - __FMODE_NONOTIFY - )); + BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ != + HWEIGHT32( + (VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) | + __FMODE_EXEC | __FMODE_NONOTIFY)); fasync_cache = kmem_cache_create("fasync_cache", sizeof(struct fasync_struct), 0, SLAB_PANIC, NULL); diff --git a/fs/file_table.c b/fs/file_table.c index ad17e05ebf95..b4baa0de4988 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -41,6 +41,141 @@ static struct kmem_cache *filp_cachep __read_mostly; static struct percpu_counter nr_files __cacheline_aligned_in_smp; +#ifdef CONFIG_FILE_TABLE_DEBUG +#include <linux/hashtable.h> +#include <mount.h> +static DEFINE_MUTEX(global_files_lock); +static DEFINE_HASHTABLE(global_files_hashtable, 10); + +struct global_filetable_lookup_key { + struct work_struct work; + uintptr_t value; +}; + +void global_filetable_print_warning_once(void) +{ + pr_err_once("\n**********************************************************\n"); + pr_err_once("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); + pr_err_once("** **\n"); + pr_err_once("** VFS FILE TABLE DEBUG is enabled . **\n"); + pr_err_once("** Allocating extra memory and slowing access to files **\n"); + pr_err_once("** **\n"); + pr_err_once("** This means that this is a DEBUG kernel and it is **\n"); + pr_err_once("** unsafe for production use. **\n"); + pr_err_once("** **\n"); + pr_err_once("** If you see this message and you are not debugging **\n"); + pr_err_once("** the kernel, report this immediately to your vendor! **\n"); + pr_err_once("** **\n"); + pr_err_once("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); + pr_err_once("**********************************************************\n"); +} + +void global_filetable_add(struct file *filp) +{ + struct mount *mnt; + + if (filp->f_path.dentry->d_iname == NULL || + strlen(filp->f_path.dentry->d_iname) == 0) + return; + + mnt = real_mount(filp->f_path.mnt); + + mutex_lock(&global_files_lock); + hash_add(global_files_hashtable, &filp->f_hash, (uintptr_t)mnt); + mutex_unlock(&global_files_lock); +} + +void global_filetable_del(struct file *filp) +{ + mutex_lock(&global_files_lock); + hash_del(&filp->f_hash); + mutex_unlock(&global_files_lock); +} + +static void global_print_file(struct file *filp, char *path_buffer, int *count) +{ + char *pathname; + + pathname = d_path(&filp->f_path, path_buffer, PAGE_SIZE); + if (IS_ERR(pathname)) + pr_err("VFS: File %d Address : %pa partial filename: %s ref_count=%ld\n", + ++(*count), &filp, filp->f_path.dentry->d_iname, + atomic_long_read(&filp->f_count)); + else + pr_err("VFS: File %d Address : %pa full filepath: %s ref_count=%ld\n", + ++(*count), &filp, pathname, + atomic_long_read(&filp->f_count)); +} + +static void global_filetable_print(uintptr_t lookup_mnt) +{ + struct hlist_node *tmp; + struct file *filp; + struct mount *mnt; + int index; + int count = 0; + char *path_buffer = (char *)__get_free_page(GFP_TEMPORARY); + + mutex_lock(&global_files_lock); + pr_err("\n**********************************************************\n"); + pr_err("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); + + pr_err("\n"); + pr_err("VFS: The following files hold a reference to the mount\n"); + pr_err("\n"); + hash_for_each_possible_safe(global_files_hashtable, filp, tmp, f_hash, + lookup_mnt) { + mnt = real_mount(filp->f_path.mnt); + if ((uintptr_t)mnt == lookup_mnt) + global_print_file(filp, path_buffer, &count); + } + pr_err("\n"); + pr_err("VFS: Found total of %d open files\n", count); + pr_err("\n"); + + count = 0; + pr_err("\n"); + pr_err("VFS: The following files need to cleaned up\n"); + pr_err("\n"); + hash_for_each_safe(global_files_hashtable, index, tmp, filp, f_hash) { + if (atomic_long_read(&filp->f_count) == 0) + global_print_file(filp, path_buffer, &count); + } + + pr_err("\n"); + pr_err("VFS: Found total of %d files awaiting clean-up\n", count); + pr_err("\n"); + pr_err("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); + pr_err("\n**********************************************************\n"); + + mutex_unlock(&global_files_lock); + free_page((unsigned long)path_buffer); +} + +static void global_filetable_print_work_fn(struct work_struct *work) +{ + struct global_filetable_lookup_key *key; + uintptr_t lookup_mnt; + + key = container_of(work, struct global_filetable_lookup_key, work); + lookup_mnt = key->value; + kfree(key); + global_filetable_print(lookup_mnt); +} + +void global_filetable_delayed_print(struct mount *mnt) +{ + struct global_filetable_lookup_key *key; + + key = kzalloc(sizeof(*key), GFP_KERNEL); + if (key == NULL) + return; + key->value = (uintptr_t)mnt; + INIT_WORK(&key->work, global_filetable_print_work_fn); + schedule_work(&key->work); +} +#endif /* CONFIG_FILE_TABLE_DEBUG */ + static void file_free_rcu(struct rcu_head *head) { struct file *f = container_of(head, struct file, f_u.fu_rcuhead); @@ -219,6 +354,7 @@ static void __fput(struct file *file) put_write_access(inode); __mnt_drop_write(mnt); } + global_filetable_del(file); file->f_path.dentry = NULL; file->f_path.mnt = NULL; file->f_inode = NULL; @@ -314,6 +450,7 @@ void __init files_init(void) filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); percpu_counter_init(&nr_files, 0, GFP_KERNEL); + global_filetable_print_warning_once(); } /* diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 9cd8c92b953d..070901e76653 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -80,9 +80,9 @@ static struct rhashtable_params ht_parms = { static struct rhashtable gl_hash_table; -void gfs2_glock_free(struct gfs2_glock *gl) +static void gfs2_glock_dealloc(struct rcu_head *rcu) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_glock *gl = container_of(rcu, struct gfs2_glock, gl_rcu); if (gl->gl_ops->go_flags & GLOF_ASPACE) { kmem_cache_free(gfs2_glock_aspace_cachep, gl); @@ -90,6 +90,13 @@ void gfs2_glock_free(struct gfs2_glock *gl) kfree(gl->gl_lksb.sb_lvbptr); kmem_cache_free(gfs2_glock_cachep, gl); } +} + +void gfs2_glock_free(struct gfs2_glock *gl) +{ + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + + call_rcu(&gl->gl_rcu, gfs2_glock_dealloc); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) wake_up(&sdp->sd_glock_wait); } diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index be519416c112..4a9077ec9313 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -367,6 +367,7 @@ struct gfs2_glock { loff_t end; } gl_vm; }; + struct rcu_head gl_rcu; struct rhash_head gl_node; }; diff --git a/fs/internal.h b/fs/internal.h index 6387b35a1c0d..1b93a3929b16 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -153,3 +153,29 @@ extern void mnt_pin_kill(struct mount *m); * fs/nsfs.c */ extern struct dentry_operations ns_dentry_operations; + +#ifdef CONFIG_FILE_TABLE_DEBUG +void global_filetable_print_warning_once(void); +void global_filetable_add(struct file *filp); +void global_filetable_del(struct file *filp); +void global_filetable_delayed_print(struct mount *mnt); + +#else /* i.e NOT CONFIG_FILE_TABLE_DEBUG */ + +static inline void global_filetable_print_warning_once(void) +{ +} + +static inline void global_filetable_add(struct file *filp) +{ +} + +static inline void global_filetable_del(struct file *filp) +{ +} + +static inline void global_filetable_delayed_print(struct mount *mnt) +{ +} + +#endif /* CONFIG_FILE_TABLE_DEBUG */ diff --git a/fs/mount.h b/fs/mount.h index 13a4ebbbaa74..37c64bbe840c 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -57,6 +57,7 @@ struct mount { struct mnt_namespace *mnt_ns; /* containing namespace */ struct mountpoint *mnt_mp; /* where is it mounted */ struct hlist_node mnt_mp_list; /* list mounts with the same mountpoint */ + struct list_head mnt_umounting; /* list entry for umount propagation */ #ifdef CONFIG_FSNOTIFY struct hlist_head mnt_fsnotify_marks; __u32 mnt_fsnotify_mask; diff --git a/fs/namei.c b/fs/namei.c index 816b6e8e934e..3b2b5c8e0ec7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3379,6 +3379,8 @@ out2: error = -ESTALE; } file = ERR_PTR(error); + } else { + global_filetable_add(file); } return file; } @@ -4262,11 +4264,11 @@ int vfs_rename2(struct vfsmount *mnt, { int error; bool is_dir = d_is_dir(old_dentry); - const unsigned char *old_name; struct inode *source = old_dentry->d_inode; struct inode *target = new_dentry->d_inode; bool new_is_dir = false; unsigned max_links = new_dir->i_sb->s_max_links; + struct name_snapshot old_name; /* * Check source == target. @@ -4320,7 +4322,7 @@ int vfs_rename2(struct vfsmount *mnt, if (error) return error; - old_name = fsnotify_oldname_init(old_dentry->d_name.name); + take_dentry_name_snapshot(&old_name, old_dentry); dget(new_dentry); if (!is_dir || (flags & RENAME_EXCHANGE)) lock_two_nondirectories(source, target); @@ -4381,14 +4383,14 @@ out: mutex_unlock(&target->i_mutex); dput(new_dentry); if (!error) { - fsnotify_move(old_dir, new_dir, old_name, is_dir, + fsnotify_move(old_dir, new_dir, old_name.name, is_dir, !(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry); if (flags & RENAME_EXCHANGE) { fsnotify_move(new_dir, old_dir, old_dentry->d_name.name, new_is_dir, NULL, new_dentry); } } - fsnotify_oldname_free(old_name); + release_dentry_name_snapshot(&old_name); return error; } diff --git a/fs/namespace.c b/fs/namespace.c index a22959c97384..f32450c3e72c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -237,6 +237,7 @@ static struct mount *alloc_vfsmnt(const char *name) INIT_LIST_HEAD(&mnt->mnt_slave_list); INIT_LIST_HEAD(&mnt->mnt_slave); INIT_HLIST_NODE(&mnt->mnt_mp_list); + INIT_LIST_HEAD(&mnt->mnt_umounting); #ifdef CONFIG_FSNOTIFY INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks); #endif @@ -1595,6 +1596,8 @@ static int do_umount(struct mount *mnt, int flags) } unlock_mount_hash(); namespace_unlock(); + if (retval == -EBUSY) + global_filetable_delayed_print(mnt); return retval; } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 5b21b1ca2341..348e0a05bd18 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1135,11 +1135,13 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) /* Force a full look up iff the parent directory has changed */ if (!nfs_is_exclusive_create(dir, flags) && nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) { - - if (nfs_lookup_verify_inode(inode, flags)) { + error = nfs_lookup_verify_inode(inode, flags); + if (error) { if (flags & LOOKUP_RCU) return -ECHILD; - goto out_zap_parent; + if (error == -ESTALE) + goto out_zap_parent; + goto out_error; } goto out_valid; } @@ -1163,8 +1165,10 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) trace_nfs_lookup_revalidate_enter(dir, dentry, flags); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label); trace_nfs_lookup_revalidate_exit(dir, dentry, flags, error); - if (error) + if (error == -ESTALE || error == -ENOENT) goto out_bad; + if (error) + goto out_error; if (nfs_compare_fh(NFS_FH(inode), fhandle)) goto out_bad; if ((error = nfs_refresh_inode(inode, fattr)) != 0) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index f714b98cfd74..668ac19af58f 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1241,9 +1241,9 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat return 0; /* Has the inode gone and changed behind our back? */ if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid) - return -EIO; + return -ESTALE; if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) - return -EIO; + return -ESTALE; if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 && inode->i_version != fattr->change_attr) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index db39de2dd4cb..a64adc2fced9 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -104,16 +104,20 @@ int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) if (unlikely(!fsnotify_inode_watches_children(p_inode))) __fsnotify_update_child_dentry_flags(p_inode); else if (p_inode->i_fsnotify_mask & mask) { + struct name_snapshot name; + /* we are notifying a parent so come up with the new mask which * specifies these are events which came from a child. */ mask |= FS_EVENT_ON_CHILD; + take_dentry_name_snapshot(&name, dentry); if (path) ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, - dentry->d_name.name, 0); + name.name, 0); else ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, - dentry->d_name.name, 0); + name.name, 0); + release_dentry_name_snapshot(&name); } dput(parent); diff --git a/fs/open.c b/fs/open.c index e70cca15c976..1fd96c5d3895 100644 --- a/fs/open.c +++ b/fs/open.c @@ -898,6 +898,12 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o int lookup_flags = 0; int acc_mode; + /* + * Clear out all open flags we don't know about so that we don't report + * them in fcntl(F_GETFD) or similar interfaces. + */ + flags &= VALID_OPEN_FLAGS; + if (flags & (O_CREAT | __O_TMPFILE)) op->mode = (mode & S_IALLUGO) | S_IFREG; else diff --git a/fs/pnode.c b/fs/pnode.c index e4e428d621e9..ddb846f878b8 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -24,6 +24,11 @@ static inline struct mount *first_slave(struct mount *p) return list_entry(p->mnt_slave_list.next, struct mount, mnt_slave); } +static inline struct mount *last_slave(struct mount *p) +{ + return list_entry(p->mnt_slave_list.prev, struct mount, mnt_slave); +} + static inline struct mount *next_slave(struct mount *p) { return list_entry(p->mnt_slave.next, struct mount, mnt_slave); @@ -164,6 +169,19 @@ static struct mount *propagation_next(struct mount *m, } } +static struct mount *skip_propagation_subtree(struct mount *m, + struct mount *origin) +{ + /* + * Advance m such that propagation_next will not return + * the slaves of m. + */ + if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list)) + m = last_slave(m); + + return m; +} + static struct mount *next_group(struct mount *m, struct mount *origin) { while (1) { @@ -415,65 +433,104 @@ void propagate_mount_unlock(struct mount *mnt) } } -/* - * Mark all mounts that the MNT_LOCKED logic will allow to be unmounted. - */ -static void mark_umount_candidates(struct mount *mnt) +static void umount_one(struct mount *mnt, struct list_head *to_umount) { - struct mount *parent = mnt->mnt_parent; - struct mount *m; - - BUG_ON(parent == mnt); - - for (m = propagation_next(parent, parent); m; - m = propagation_next(m, parent)) { - struct mount *child = __lookup_mnt(&m->mnt, - mnt->mnt_mountpoint); - if (!child || (child->mnt.mnt_flags & MNT_UMOUNT)) - continue; - if (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m)) { - SET_MNT_MARK(child); - } - } + CLEAR_MNT_MARK(mnt); + mnt->mnt.mnt_flags |= MNT_UMOUNT; + list_del_init(&mnt->mnt_child); + list_del_init(&mnt->mnt_umounting); + list_move_tail(&mnt->mnt_list, to_umount); } /* * NOTE: unmounting 'mnt' naturally propagates to all other mounts its * parent propagates to. */ -static void __propagate_umount(struct mount *mnt) +static bool __propagate_umount(struct mount *mnt, + struct list_head *to_umount, + struct list_head *to_restore) { - struct mount *parent = mnt->mnt_parent; - struct mount *m; + bool progress = false; + struct mount *child; - BUG_ON(parent == mnt); + /* + * The state of the parent won't change if this mount is + * already unmounted or marked as without children. + */ + if (mnt->mnt.mnt_flags & (MNT_UMOUNT | MNT_MARKED)) + goto out; - for (m = propagation_next(parent, parent); m; - m = propagation_next(m, parent)) { - struct mount *topper; - struct mount *child = __lookup_mnt(&m->mnt, - mnt->mnt_mountpoint); - /* - * umount the child only if the child has no children - * and the child is marked safe to unmount. - */ - if (!child || !IS_MNT_MARKED(child)) + /* Verify topper is the only grandchild that has not been + * speculatively unmounted. + */ + list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) { + if (child->mnt_mountpoint == mnt->mnt.mnt_root) continue; - CLEAR_MNT_MARK(child); + if (!list_empty(&child->mnt_umounting) && IS_MNT_MARKED(child)) + continue; + /* Found a mounted child */ + goto children; + } - /* If there is exactly one mount covering all of child - * replace child with that mount. - */ - topper = find_topper(child); - if (topper) - mnt_change_mountpoint(child->mnt_parent, child->mnt_mp, - topper); + /* Mark mounts that can be unmounted if not locked */ + SET_MNT_MARK(mnt); + progress = true; + + /* If a mount is without children and not locked umount it. */ + if (!IS_MNT_LOCKED(mnt)) { + umount_one(mnt, to_umount); + } else { +children: + list_move_tail(&mnt->mnt_umounting, to_restore); + } +out: + return progress; +} + +static void umount_list(struct list_head *to_umount, + struct list_head *to_restore) +{ + struct mount *mnt, *child, *tmp; + list_for_each_entry(mnt, to_umount, mnt_list) { + list_for_each_entry_safe(child, tmp, &mnt->mnt_mounts, mnt_child) { + /* topper? */ + if (child->mnt_mountpoint == mnt->mnt.mnt_root) + list_move_tail(&child->mnt_umounting, to_restore); + else + umount_one(child, to_umount); + } + } +} - if (list_empty(&child->mnt_mounts)) { - list_del_init(&child->mnt_child); - child->mnt.mnt_flags |= MNT_UMOUNT; - list_move_tail(&child->mnt_list, &mnt->mnt_list); +static void restore_mounts(struct list_head *to_restore) +{ + /* Restore mounts to a clean working state */ + while (!list_empty(to_restore)) { + struct mount *mnt, *parent; + struct mountpoint *mp; + + mnt = list_first_entry(to_restore, struct mount, mnt_umounting); + CLEAR_MNT_MARK(mnt); + list_del_init(&mnt->mnt_umounting); + + /* Should this mount be reparented? */ + mp = mnt->mnt_mp; + parent = mnt->mnt_parent; + while (parent->mnt.mnt_flags & MNT_UMOUNT) { + mp = parent->mnt_mp; + parent = parent->mnt_parent; } + if (parent != mnt->mnt_parent) + mnt_change_mountpoint(parent, mp, mnt); + } +} + +static void cleanup_umount_visitations(struct list_head *visited) +{ + while (!list_empty(visited)) { + struct mount *mnt = + list_first_entry(visited, struct mount, mnt_umounting); + list_del_init(&mnt->mnt_umounting); } } @@ -487,12 +544,69 @@ static void __propagate_umount(struct mount *mnt) int propagate_umount(struct list_head *list) { struct mount *mnt; + LIST_HEAD(to_restore); + LIST_HEAD(to_umount); + LIST_HEAD(visited); + + /* Find candidates for unmounting */ + list_for_each_entry_reverse(mnt, list, mnt_list) { + struct mount *parent = mnt->mnt_parent; + struct mount *m; + + /* + * If this mount has already been visited it is known that it's + * entire peer group and all of their slaves in the propagation + * tree for the mountpoint has already been visited and there is + * no need to visit them again. + */ + if (!list_empty(&mnt->mnt_umounting)) + continue; + + list_add_tail(&mnt->mnt_umounting, &visited); + for (m = propagation_next(parent, parent); m; + m = propagation_next(m, parent)) { + struct mount *child = __lookup_mnt(&m->mnt, + mnt->mnt_mountpoint); + if (!child) + continue; + + if (!list_empty(&child->mnt_umounting)) { + /* + * If the child has already been visited it is + * know that it's entire peer group and all of + * their slaves in the propgation tree for the + * mountpoint has already been visited and there + * is no need to visit this subtree again. + */ + m = skip_propagation_subtree(m, parent); + continue; + } else if (child->mnt.mnt_flags & MNT_UMOUNT) { + /* + * We have come accross an partially unmounted + * mount in list that has not been visited yet. + * Remember it has been visited and continue + * about our merry way. + */ + list_add_tail(&child->mnt_umounting, &visited); + continue; + } + + /* Check the child and parents while progress is made */ + while (__propagate_umount(child, + &to_umount, &to_restore)) { + /* Is the parent a umount candidate? */ + child = child->mnt_parent; + if (list_empty(&child->mnt_umounting)) + break; + } + } + } - list_for_each_entry_reverse(mnt, list, mnt_list) - mark_umount_candidates(mnt); + umount_list(&to_umount, &to_restore); + restore_mounts(&to_restore); + cleanup_umount_visitations(&visited); + list_splice_tail(&to_umount, list); - list_for_each_entry(mnt, list, mnt_list) - __propagate_umount(mnt); return 0; } diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 8d1e5e2db6a1..c9e4bc47c79d 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -434,7 +434,7 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt, for (i = 0; i < cxt->max_dump_cnt; i++) { cxt->przs[i] = persistent_ram_new(*paddr, cxt->record_size, 0, &cxt->ecc_info, - cxt->memtype); + cxt->memtype, 0); if (IS_ERR(cxt->przs[i])) { err = PTR_ERR(cxt->przs[i]); dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n", @@ -471,7 +471,8 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, return -ENOMEM; } - *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info, cxt->memtype); + *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info, + cxt->memtype, 0); if (IS_ERR(*prz)) { int err = PTR_ERR(*prz); diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index 3975deec02f8..e11672aa4575 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -48,16 +48,15 @@ static inline size_t buffer_start(struct persistent_ram_zone *prz) return atomic_read(&prz->buffer->start); } -static DEFINE_RAW_SPINLOCK(buffer_lock); - /* increase and wrap the start pointer, returning the old value */ static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a) { int old; int new; - unsigned long flags; + unsigned long flags = 0; - raw_spin_lock_irqsave(&buffer_lock, flags); + if (!(prz->flags & PRZ_FLAG_NO_LOCK)) + raw_spin_lock_irqsave(&prz->buffer_lock, flags); old = atomic_read(&prz->buffer->start); new = old + a; @@ -65,7 +64,8 @@ static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a) new -= prz->buffer_size; atomic_set(&prz->buffer->start, new); - raw_spin_unlock_irqrestore(&buffer_lock, flags); + if (!(prz->flags & PRZ_FLAG_NO_LOCK)) + raw_spin_unlock_irqrestore(&prz->buffer_lock, flags); return old; } @@ -75,9 +75,10 @@ static void buffer_size_add(struct persistent_ram_zone *prz, size_t a) { size_t old; size_t new; - unsigned long flags; + unsigned long flags = 0; - raw_spin_lock_irqsave(&buffer_lock, flags); + if (!(prz->flags & PRZ_FLAG_NO_LOCK)) + raw_spin_lock_irqsave(&prz->buffer_lock, flags); old = atomic_read(&prz->buffer->size); if (old == prz->buffer_size) @@ -89,7 +90,8 @@ static void buffer_size_add(struct persistent_ram_zone *prz, size_t a) atomic_set(&prz->buffer->size, new); exit: - raw_spin_unlock_irqrestore(&buffer_lock, flags); + if (!(prz->flags & PRZ_FLAG_NO_LOCK)) + raw_spin_unlock_irqrestore(&prz->buffer_lock, flags); } static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz, @@ -491,6 +493,7 @@ static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig, prz->buffer->sig); } + /* Rewind missing or invalid memory area. */ prz->buffer->sig = sig; persistent_ram_zap(prz); @@ -517,7 +520,7 @@ void persistent_ram_free(struct persistent_ram_zone *prz) struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, u32 sig, struct persistent_ram_ecc_info *ecc_info, - unsigned int memtype) + unsigned int memtype, u32 flags) { struct persistent_ram_zone *prz; int ret = -ENOMEM; @@ -528,6 +531,10 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, goto err; } + /* Initialize general buffer state. */ + raw_spin_lock_init(&prz->buffer_lock); + prz->flags = flags; + ret = persistent_ram_buffer_map(start, size, prz, memtype); if (ret) goto err; diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 60fea424835f..103dc45a131f 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -766,13 +766,9 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * afterwards in the other cases: we fsstack_copy_inode_size from * the lower level. */ - if (current->mm) - down_write(¤t->mm->mmap_sem); if (ia->ia_valid & ATTR_SIZE) { err = inode_newsize_ok(&tmp, ia->ia_size); if (err) { - if (current->mm) - up_write(¤t->mm->mmap_sem); goto out; } truncate_setsize(inode, ia->ia_size); @@ -795,8 +791,6 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */ NULL); mutex_unlock(&d_inode(lower_dentry)->i_mutex); - if (current->mm) - up_write(¤t->mm->mmap_sem); if (err) goto out; diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 3c5b51d49d21..80825b287836 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -364,41 +364,34 @@ out: return err; } -/* A feature which supports mount_nodev() with options */ -static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, - struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, - int (*fill_super)(struct vfsmount *, struct super_block *, - const char *, void *, int)) +struct sdcardfs_mount_private { + struct vfsmount *mnt; + const char *dev_name; + void *raw_data; +}; +static int __sdcardfs_fill_super( + struct super_block *sb, + void *_priv, int silent) { - int error; - struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL); - - if (IS_ERR(s)) - return ERR_CAST(s); - - s->s_flags = flags; + struct sdcardfs_mount_private *priv = _priv; - error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0); - if (error) { - deactivate_locked_super(s); - return ERR_PTR(error); - } - s->s_flags |= MS_ACTIVE; - return dget(s->s_root); + return sdcardfs_read_super(priv->mnt, + sb, priv->dev_name, priv->raw_data, silent); } static struct dentry *sdcardfs_mount(struct vfsmount *mnt, struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data) { - /* - * dev_name is a lower_path_name, - * raw_data is a option string. - */ - return mount_nodev_with_options(mnt, fs_type, flags, dev_name, - raw_data, sdcardfs_read_super); + struct sdcardfs_mount_private priv = { + .mnt = mnt, + .dev_name = dev_name, + .raw_data = raw_data + }; + + return mount_nodev(fs_type, flags, + &priv, __sdcardfs_fill_super); } static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, @@ -423,7 +416,7 @@ void sdcardfs_kill_sb(struct super_block *sb) list_del(&sbi->list); mutex_unlock(&sdcardfs_super_list_lock); } - generic_shutdown_super(sb); + kill_anon_super(sb); } static struct file_system_type sdcardfs_fs_type = { diff --git a/fs/seq_file.c b/fs/seq_file.c index d672e2fec459..6dc4296eed62 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -72,9 +72,10 @@ int seq_open(struct file *file, const struct seq_operations *op) mutex_init(&p->lock); p->op = op; -#ifdef CONFIG_USER_NS - p->user_ns = file->f_cred->user_ns; -#endif + + // No refcounting: the lifetime of 'p' is constrained + // to the lifetime of the file. + p->file = file; /* * Wrappers around seq_open(e.g. swaps_open) need to be diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 7be3166ba553..0e659d9c69a1 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1235,8 +1235,8 @@ int udf_setsize(struct inode *inode, loff_t newsize) return err; } set_size: - truncate_setsize(inode, newsize); up_write(&iinfo->i_data_sem); + truncate_setsize(inode, newsize); } else { if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { down_write(&iinfo->i_data_sem); @@ -1253,9 +1253,9 @@ set_size: udf_get_block); if (err) return err; + truncate_setsize(inode, newsize); down_write(&iinfo->i_data_sem); udf_clear_extent_cache(inode); - truncate_setsize(inode, newsize); udf_truncate_extents(inode); up_write(&iinfo->i_data_sem); } diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 187b80267ff9..a9063ac50c4e 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -1426,6 +1426,26 @@ __xfs_get_blocks( if (error) goto out_unlock; + /* + * The only time we can ever safely find delalloc blocks on direct I/O + * is a dio write to post-eof speculative preallocation. All other + * scenarios are indicative of a problem or misuse (such as mixing + * direct and mapped I/O). + * + * The file may be unmapped by the time we get here so we cannot + * reliably fail the I/O based on mapping. Instead, fail the I/O if this + * is a read or a write within eof. Otherwise, carry on but warn as a + * precuation if the file happens to be mapped. + */ + if (direct && imap.br_startblock == DELAYSTARTBLOCK) { + if (!create || offset < i_size_read(VFS_I(ip))) { + WARN_ON_ONCE(1); + error = -EIO; + goto out_unlock; + } + WARN_ON_ONCE(mapping_mapped(VFS_I(ip)->i_mapping)); + } + /* for DAX, we convert unwritten extents directly */ if (create && (!nimaps || @@ -1525,7 +1545,6 @@ __xfs_get_blocks( set_buffer_new(bh_result); if (imap.br_startblock == DELAYSTARTBLOCK) { - BUG_ON(direct); if (create) { set_buffer_uptodate(bh_result); set_buffer_mapped(bh_result); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 6555fdbee88a..f710a7075c0e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -652,12 +652,15 @@ struct drm_encoder { * @pt_scan_info: PT scan info obtained from the VCDB of EDID * @it_scan_info: IT scan info obtained from the VCDB of EDID * @ce_scan_info: CE scan info obtained from the VCDB of EDID + * @color_enc_fmt: Colorimetry encoding formats of sink * @hdr_eotf: Electro optical transfer function obtained from HDR block * @hdr_metadata_type_one: Metadata type one obtained from HDR block * @hdr_max_luminance: desired max luminance obtained from HDR block * @hdr_avg_luminance: desired avg luminance obtained from HDR block * @hdr_min_luminance: desired min luminance obtained from HDR block * @hdr_supported: does the sink support HDR content + * @rgb_qs: does the sink declare RGB selectable quantization range + * @yuv_qs: does the sink declare YCC selectable quantization range * @edid_corrupt: indicates whether the last read EDID was corrupt * @debugfs_entry: debugfs directory for this connector * @state: current atomic state for this connector @@ -740,12 +743,15 @@ struct drm_connector { u8 pt_scan_info; u8 it_scan_info; u8 ce_scan_info; + u8 color_enc_fmt; u32 hdr_eotf; bool hdr_metadata_type_one; u32 hdr_max_luminance; u32 hdr_avg_luminance; u32 hdr_min_luminance; bool hdr_supported; + bool rgb_qs; + bool yuv_qs; /* Flag for raw EDID header corruption - used in Displayport * compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6 */ diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 69cb2ba37116..c0884e120041 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -214,6 +214,15 @@ struct detailed_timing { #define DRM_EDID_YCBCR420_DC_36 (1 << 1) #define DRM_EDID_YCBCR420_DC_30 (1 << 0) +#define DRM_EDID_COLORIMETRY_xvYCC_601 (1 << 0) +#define DRM_EDID_COLORIMETRY_xvYCC_709 (1 << 1) +#define DRM_EDID_COLORIMETRY_sYCC_601 (1 << 2) +#define DRM_EDID_COLORIMETRY_ADBYCC_601 (1 << 3) +#define DRM_EDID_COLORIMETRY_ADB_RGB (1 << 4) +#define DRM_EDID_COLORIMETRY_BT2020_CYCC (1 << 5) +#define DRM_EDID_COLORIMETRY_BT2020_YCC (1 << 6) +#define DRM_EDID_COLORIMETRY_BT2020_RGB (1 << 7) + /* ELD Header Block */ #define DRM_ELD_HEADER_BLOCK_SIZE 4 diff --git a/include/dt-bindings/clock/msm-clocks-8998.h b/include/dt-bindings/clock/msm-clocks-8998.h index cd36374a04a7..67e47c46e09a 100644 --- a/include/dt-bindings/clock/msm-clocks-8998.h +++ b/include/dt-bindings/clock/msm-clocks-8998.h @@ -443,6 +443,11 @@ #define clk_dsi0pll_pclk_src 0x5efd85d4 #define clk_dsi0pll_pclk_src_mux 0x84b14663 #define clk_dsi0pll_post_bit_div 0xf46dcf27 +#define clk_dsi0pll_pll_out_div1 0xeda5b7fe +#define clk_dsi0pll_pll_out_div2 0x97fa476d +#define clk_dsi0pll_pll_out_div4 0x90a98ce0 +#define clk_dsi0pll_pll_out_div8 0x9d9d85cf +#define clk_dsi0pll_pll_out_mux 0x179c27ca #define clk_dsi0pll_post_vco_mux 0xfaf9bd1f #define clk_dsi0pll_post_vco_div1 0xabb50b2a #define clk_dsi0pll_post_vco_div4 0xbe51c091 @@ -455,6 +460,11 @@ #define clk_dsi1pll_pclk_src 0xeddcd80e #define clk_dsi1pll_pclk_src_mux 0x3651feb3 #define clk_dsi1pll_post_bit_div 0x712f0260 +#define clk_dsi1pll_pll_out_div8 0x87628ddb +#define clk_dsi1pll_pll_out_div4 0x0d9a384b +#define clk_dsi1pll_pll_out_div2 0x0c9b5748 +#define clk_dsi1pll_pll_out_div1 0x3193164e +#define clk_dsi1pll_pll_out_mux 0x171bf8fd #define clk_dsi1pll_post_vco_mux 0xc6a90d20 #define clk_dsi1pll_post_vco_div1 0x6f47ca7d #define clk_dsi1pll_post_vco_div4 0x90628974 diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 2043af2141c1..01448e01b40d 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -616,5 +616,11 @@ static inline struct inode *d_real_inode(struct dentry *dentry) return d_backing_inode(d_real(dentry)); } +struct name_snapshot { + const char *name; + char inline_name[DNAME_INLINE_LEN]; +}; +void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *); +void release_dentry_name_snapshot(struct name_snapshot *); #endif /* __LINUX_DCACHE_H */ diff --git a/include/linux/device.h b/include/linux/device.h index 4b4e2d5ce6e7..30c52d70c86d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -368,6 +368,7 @@ int subsys_virtual_register(struct bus_type *subsys, * @suspend: Used to put the device to sleep mode, usually to a low power * state. * @resume: Used to bring the device from the sleep mode. + * @shutdown: Called at shut-down time to quiesce the device. * @ns_type: Callbacks so sysfs can detemine namespaces. * @namespace: Namespace of the device belongs to this class. * @pm: The default device power management operations of this class. @@ -396,6 +397,7 @@ struct class { int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); + int (*shutdown)(struct device *dev); const struct kobj_ns_type_operations *ns_type; const void *(*namespace)(struct device *dev); diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index 76ce329e656d..1b48d9c9a561 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -3,6 +3,12 @@ #include <uapi/linux/fcntl.h> +/* list of all valid flags for the open/openat flags argument: */ +#define VALID_OPEN_FLAGS \ + (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \ + O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | __O_SYNC | O_DSYNC | \ + FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \ + O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE) #ifndef force_o_largefile #define force_o_largefile() (BITS_PER_LONG != 32) diff --git a/include/linux/fs.h b/include/linux/fs.h index 4b27be2038e3..df1171bada01 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -912,6 +912,10 @@ struct file { struct list_head f_tfile_llink; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; + +#ifdef CONFIG_FILE_TABLE_DEBUG + struct hlist_node f_hash; +#endif /* #ifdef CONFIG_FILE_TABLE_DEBUG */ } __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */ struct file_handle { diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 7ee1774edee5..a7789559078b 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -310,35 +310,4 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid) } } -#if defined(CONFIG_FSNOTIFY) /* notify helpers */ - -/* - * fsnotify_oldname_init - save off the old filename before we change it - */ -static inline const unsigned char *fsnotify_oldname_init(const unsigned char *name) -{ - return kstrdup(name, GFP_KERNEL); -} - -/* - * fsnotify_oldname_free - free the name we got from fsnotify_oldname_init - */ -static inline void fsnotify_oldname_free(const unsigned char *old_name) -{ - kfree(old_name); -} - -#else /* CONFIG_FSNOTIFY */ - -static inline const char *fsnotify_oldname_init(const unsigned char *name) -{ - return NULL; -} - -static inline void fsnotify_oldname_free(const unsigned char *old_name) -{ -} - -#endif /* CONFIG_FSNOTIFY */ - #endif /* _LINUX_FS_NOTIFY_H */ diff --git a/include/linux/ipa.h b/include/linux/ipa.h index c11a5c4afece..a4b817c5e4fc 100644 --- a/include/linux/ipa.h +++ b/include/linux/ipa.h @@ -40,6 +40,14 @@ enum ipa_nat_en_type { }; /** + * enum ipa_ipv6ct_en_type - IPv6CT setting type in IPA end-point + */ +enum ipa_ipv6ct_en_type { + IPA_BYPASS_IPV6CT, + IPA_ENABLE_IPV6CT, +}; + +/** * enum ipa_mode_type - mode setting type in IPA end-point * @BASIC: basic mode * @ENABLE_FRAMING_HDLC: not currently supported @@ -119,6 +127,19 @@ struct ipa_ep_cfg_nat { }; /** + * struct ipa_ep_cfg_conn_track - IPv6 Connection tracking configuration in + * IPA end-point + * @conn_track_en: Defines speculative conn_track action, means if specific + * pipe needs to have UL/DL IPv6 Connection Tracking or Bypass + * IPv6 Connection Tracking. 0: Bypass IPv6 Connection Tracking + * 1: IPv6 UL/DL Connection Tracking. + * Valid for Input Pipes only (IPA consumer) + */ +struct ipa_ep_cfg_conn_track { + enum ipa_ipv6ct_en_type conn_track_en; +}; + +/** * struct ipa_ep_cfg_hdr - header configuration in IPA end-point * * @hdr_len:Header length in bytes to be added/removed. Assuming @@ -386,7 +407,8 @@ struct ipa_ep_cfg_seq { /** * struct ipa_ep_cfg - configuration of IPA end-point - * @nat: NAT parmeters + * @nat: NAT parameters + * @conn_track: IPv6CT parameters * @hdr: Header parameters * @hdr_ext: Extended header parameters * @mode: Mode parameters @@ -400,6 +422,7 @@ struct ipa_ep_cfg_seq { */ struct ipa_ep_cfg { struct ipa_ep_cfg_nat nat; + struct ipa_ep_cfg_conn_track conn_track; struct ipa_ep_cfg_hdr hdr; struct ipa_ep_cfg_hdr_ext hdr_ext; struct ipa_ep_cfg_mode mode; diff --git a/include/linux/msm_audio_ion.h b/include/linux/msm_audio_ion.h index 0761b880ca88..9e77ac317c28 100644 --- a/include/linux/msm_audio_ion.h +++ b/include/linux/msm_audio_ion.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,12 @@ #include <sound/pcm.h> #include <linux/msm_ion.h> +enum { + HLOS_TO_ADSP = 1, + ADSP_TO_HLOS, +}; + +#define VMID_CP_ADSP_SHARED 33 int msm_audio_ion_alloc(const char *name, struct ion_client **client, struct ion_handle **handle, size_t bufsz, @@ -26,6 +32,7 @@ int msm_audio_ion_import(const char *name, struct ion_client **client, struct ion_handle **handle, int fd, unsigned long *ionflag, size_t bufsz, ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr); + int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle); int msm_audio_ion_mmap(struct audio_buffer *substream, struct vm_area_struct *vma); @@ -42,4 +49,13 @@ int msm_audio_ion_import_legacy(const char *name, struct ion_client *client, int msm_audio_ion_free_legacy(struct ion_client *client, struct ion_handle *handle); u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa); + +int msm_audio_ion_phys_assign(const char *name, struct ion_client **client, + struct ion_handle **handle, + int fd, ion_phys_addr_t *paddr, + size_t *pa_len, u8 assign_type); +int msm_audio_ion_phys_free(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *paddr, + size_t *pa_len, u8 assign_type); #endif /* _LINUX_MSM_AUDIO_ION_H */ diff --git a/include/linux/phy.h b/include/linux/phy.h index 05fde31b6dc6..b64825d6ad26 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -785,6 +785,10 @@ int genphy_read_status(struct phy_device *phydev); int genphy_suspend(struct phy_device *phydev); int genphy_resume(struct phy_device *phydev); int genphy_soft_reset(struct phy_device *phydev); +static inline int genphy_no_soft_reset(struct phy_device *phydev) +{ + return 0; +} void phy_driver_unregister(struct phy_driver *drv); void phy_drivers_unregister(struct phy_driver *drv, int n); int phy_driver_register(struct phy_driver *new_driver); diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index b584e353306d..982b93ccfbe4 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -249,6 +249,12 @@ enum power_supply_property { POWER_SUPPLY_PROP_HW_CURRENT_MAX, POWER_SUPPLY_PROP_REAL_TYPE, POWER_SUPPLY_PROP_PR_SWAP, + POWER_SUPPLY_PROP_CC_STEP, + POWER_SUPPLY_PROP_CC_STEP_SEL, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, + POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, + POWER_SUPPLY_PROP_SDP_CURRENT_MAX, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 45ac5a0d29ee..7097a45dbc25 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -24,6 +24,13 @@ #include <linux/list.h> #include <linux/types.h> +/* + * Choose whether access to the RAM zone requires locking or not. If a zone + * can be written to from different CPUs like with ftrace for example, then + * PRZ_FLAG_NO_LOCK is used. For all other cases, locking is required. + */ +#define PRZ_FLAG_NO_LOCK BIT(0) + struct persistent_ram_buffer; struct rs_control; @@ -40,6 +47,8 @@ struct persistent_ram_zone { void *vaddr; struct persistent_ram_buffer *buffer; size_t buffer_size; + u32 flags; + raw_spinlock_t buffer_lock; /* ECC correction */ char *par_buffer; @@ -55,7 +64,7 @@ struct persistent_ram_zone { struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, u32 sig, struct persistent_ram_ecc_info *ecc_info, - unsigned int memtype); + unsigned int memtype, u32 flags); void persistent_ram_free(struct persistent_ram_zone *prz); void persistent_ram_zap(struct persistent_ram_zone *prz); diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h index b025df568259..bbc4625a2d39 100644 --- a/include/linux/qpnp/qpnp-revid.h +++ b/include/linux/qpnp/qpnp-revid.h @@ -252,6 +252,7 @@ struct pmic_revid_data { u8 pmic_subtype; const char *pmic_name; int fab_id; + int tp_rev; }; #ifdef CONFIG_QPNP_REVID diff --git a/include/linux/sched.h b/include/linux/sched.h index 57042d91ae9c..baf107821d9a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2481,6 +2481,7 @@ extern void do_set_cpus_allowed(struct task_struct *p, extern int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask); +extern bool cpupri_check_rt(void); #else static inline void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask) @@ -2493,6 +2494,10 @@ static inline int set_cpus_allowed_ptr(struct task_struct *p, return -EINVAL; return 0; } +static inline bool cpupri_check_rt(void) +{ + return false; +} #endif struct sched_load { diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index dde00defbaa5..f3d45dd42695 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -7,13 +7,10 @@ #include <linux/mutex.h> #include <linux/cpumask.h> #include <linux/nodemask.h> +#include <linux/fs.h> +#include <linux/cred.h> struct seq_operations; -struct file; -struct path; -struct inode; -struct dentry; -struct user_namespace; struct seq_file { char *buf; @@ -27,9 +24,7 @@ struct seq_file { struct mutex lock; const struct seq_operations *op; int poll_event; -#ifdef CONFIG_USER_NS - struct user_namespace *user_ns; -#endif + const struct file *file; void *private; }; @@ -147,7 +142,7 @@ int seq_release_private(struct inode *, struct file *); static inline struct user_namespace *seq_user_ns(struct seq_file *seq) { #ifdef CONFIG_USER_NS - return seq->user_ns; + return seq->file->f_cred->user_ns; #else extern struct user_namespace init_user_ns; return &init_user_ns; diff --git a/include/linux/tick.h b/include/linux/tick.h index 1732697ea419..d1162e9b7a36 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -105,6 +105,7 @@ extern void tick_nohz_idle_enter(void); extern void tick_nohz_idle_exit(void); extern void tick_nohz_irq_exit(void); extern ktime_t tick_nohz_get_sleep_length(void); +extern unsigned long tick_nohz_get_idle_calls(void); extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time); extern u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time); #else /* !CONFIG_NO_HZ_COMMON */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index e888eb9a2eb9..dff7adbc60bb 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -579,9 +579,9 @@ extern void usb_ep0_reinit(struct usb_device *); ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) #define EndpointRequest \ - ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) #define EndpointOutRequest \ - ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) /* class requests from the USB 2.0 hub spec, table 11-15 */ /* GetBusState and SetHubDescriptor are optional, omitted */ diff --git a/include/linux/usb/usb_qdss.h b/include/linux/usb/usb_qdss.h index e01e6781eb21..f2b8782528a8 100644 --- a/include/linux/usb/usb_qdss.h +++ b/include/linux/usb/usb_qdss.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2013, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,6 +15,9 @@ #include <linux/kernel.h> +#define USB_QDSS_CH_MDM "qdss_mdm" +#define USB_QDSS_CH_MSM "qdss" + struct qdss_request { char *buf; int length; diff --git a/include/linux/usb_bam.h b/include/linux/usb_bam.h index e65fb12c1410..b5b9edaab783 100644 --- a/include/linux/usb_bam.h +++ b/include/linux/usb_bam.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -191,7 +191,7 @@ struct usb_bam_pipe_connect { }; /** - * struct msm_usb_bam_platform_data: pipe connection information + * struct msm_usb_bam_data: pipe connection information * between USB/HSIC BAM and another BAM. USB/HSIC BAM can be * either src BAM or dst BAM * @usb_bam_num_pipes: max number of pipes to use. @@ -211,7 +211,7 @@ struct usb_bam_pipe_connect { * can work at in bam2bam mode when connected to SS host. * @enable_hsusb_bam_on_boot: Enable HSUSB BAM (non-NDP) on bootup itself */ -struct msm_usb_bam_platform_data { +struct msm_usb_bam_data { u8 max_connections; int usb_bam_num_pipes; phys_addr_t usb_bam_fifo_baseaddr; diff --git a/include/linux/vfio.h b/include/linux/vfio.h index ddb440975382..34851bf2e2c8 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -85,6 +85,8 @@ extern void vfio_unregister_iommu_driver( */ extern struct vfio_group *vfio_group_get_external_user(struct file *filep); extern void vfio_group_put_external_user(struct vfio_group *group); +extern bool vfio_external_group_match_file(struct vfio_group *group, + struct file *filep); extern int vfio_external_user_iommu_id(struct vfio_group *group); extern long vfio_external_check_extension(struct vfio_group *group, unsigned long arg); diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h index c93364b861d9..bc838936d2c9 100644 --- a/include/linux/wcnss_wlan.h +++ b/include/linux/wcnss_wlan.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -74,6 +74,8 @@ enum { #define HAVE_WCNSS_CAL_DOWNLOAD 1 #define HAVE_CBC_DONE 1 #define HAVE_WCNSS_RX_BUFF_COUNT 1 +#define HAVE_WCNSS_SNOC_HIGH_FREQ_VOTING 1 +#define HAVE_WCNSS_5G_DISABLE 1 #define WLAN_MAC_ADDR_SIZE (6) #define WLAN_RF_REG_ADDR_START_OFFSET 0x3 #define WLAN_RF_REG_DATA_START_OFFSET 0xf @@ -132,12 +134,16 @@ void wcnss_riva_dump_pmic_regs(void); int wcnss_xo_auto_detect_enabled(void); u32 wcnss_get_wlan_rx_buff_count(void); int wcnss_wlan_iris_xo_mode(void); +int wcnss_wlan_dual_band_disabled(void); void wcnss_flush_work(struct work_struct *work); void wcnss_flush_delayed_work(struct delayed_work *dwork); void wcnss_init_work(struct work_struct *work , void *callbackptr); void wcnss_init_delayed_work(struct delayed_work *dwork , void *callbackptr); int wcnss_get_iris_name(char *iris_version); void wcnss_dump_stack(struct task_struct *task); +void wcnss_snoc_vote(bool clk_chk_en); +int wcnss_parse_voltage_regulator(struct wcnss_wlan_config *wlan_config, + struct device *dev); #ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE void wcnss_log_debug_regs_on_bite(void); diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 814a13d22df6..f9bdfb096579 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -21,6 +21,7 @@ struct route_info { #include <net/flow.h> #include <net/ip6_fib.h> #include <net/sock.h> +#include <net/lwtunnel.h> #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/route.h> @@ -209,4 +210,11 @@ static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt, return daddr; } +static inline bool rt6_duplicate_nexthop(struct rt6_info *a, struct rt6_info *b) +{ + return a->dst.dev == b->dst.dev && + a->rt6i_idev == b->rt6i_idev && + ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) && + !lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate); +} #endif diff --git a/include/soc/qcom/qseecomi.h b/include/soc/qcom/qseecomi.h index 6497d962e347..e199978302bf 100644 --- a/include/soc/qcom/qseecomi.h +++ b/include/soc/qcom/qseecomi.h @@ -336,7 +336,7 @@ __packed struct qseecom_client_send_fsm_key_req { __packed struct qseecom_continue_blocked_request_ireq { uint32_t qsee_cmd_id; - uint32_t app_id; + uint32_t app_or_session_id; /*legacy: app_id; smcinvoke: session_id*/ }; @@ -681,6 +681,9 @@ __packed struct qseecom_continue_blocked_request_ireq { #define TZ_OS_CONTINUE_BLOCKED_REQUEST_ID \ TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_QSEE_OS, TZ_SVC_LISTENER, 0x04) +#define TZ_OS_CONTINUE_BLOCKED_REQUEST_SMCINVOKE_ID \ + TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_QSEE_OS, TZ_SVC_LISTENER, 0x07) + #define TZ_OS_CONTINUE_BLOCKED_REQUEST_ID_PARAM_ID \ TZ_SYSCALL_CREATE_PARAM_ID_1(TZ_SYSCALL_PARAM_TYPE_VAL) diff --git a/include/soc/qcom/ramdump.h b/include/soc/qcom/ramdump.h index 50a17c8ad605..4e23ccf269a7 100644 --- a/include/soc/qcom/ramdump.h +++ b/include/soc/qcom/ramdump.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -16,6 +16,7 @@ struct device; struct ramdump_segment { + char *name; unsigned long address; void *v_address; unsigned long size; @@ -28,6 +29,8 @@ extern int do_ramdump(void *handle, struct ramdump_segment *segments, int nsegments); extern int do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments); +extern int do_minidump(void *handle, struct ramdump_segment *segments, + int nsegments); #else static inline void *create_ramdump_device(const char *dev_name, diff --git a/include/soc/qcom/scm.h b/include/soc/qcom/scm.h index af389305207f..f0a3124dae00 100644 --- a/include/soc/qcom/scm.h +++ b/include/soc/qcom/scm.h @@ -121,9 +121,9 @@ extern s32 scm_call_atomic5_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, u32 arg3, extern u32 scm_get_version(void); extern int scm_is_call_available(u32 svc_id, u32 cmd_id); -extern int scm_get_feat_version(u32 feat); +extern int scm_get_feat_version(u32 feat, u64 *scm_ret); extern bool is_scm_armv8(void); -extern int scm_restore_sec_cfg(u32 device_id, u32 spare, int *scm_ret); +extern int scm_restore_sec_cfg(u32 device_id, u32 spare, u64 *scm_ret); extern u32 scm_io_read(phys_addr_t address); extern int scm_io_write(phys_addr_t address, u32 val); extern bool scm_is_secure_device(void); @@ -205,7 +205,7 @@ static inline int scm_is_call_available(u32 svc_id, u32 cmd_id) return 0; } -static inline int scm_get_feat_version(u32 feat) +static inline int scm_get_feat_version(u32 feat, u64 *scm_ret) { return 0; } @@ -215,7 +215,7 @@ static inline bool is_scm_armv8(void) return true; } -static inline int scm_restore_sec_cfg(u32 device_id, u32 spare, int *scm_ret) +static inline int scm_restore_sec_cfg(u32 device_id, u32 spare, u64 *scm_ret) { return 0; } diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h index 2e8d71754c98..9110963d0e9f 100644 --- a/include/soc/qcom/socinfo.h +++ b/include/soc/qcom/socinfo.h @@ -96,6 +96,10 @@ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm660") #define early_machine_is_sda660() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sda660") +#define early_machine_is_sdm636() \ + of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm636") +#define early_machine_is_sda636() \ + of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sda636") #define early_machine_is_sdm658() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm658") #define early_machine_is_sda658() \ @@ -142,6 +146,8 @@ #define early_machine_is_msmhamster() 0 #define early_machine_is_sdm660() 0 #define early_machine_is_sda660() 0 +#define early_machine_is_sdm636() 0 +#define early_machine_is_sda636() 0 #define early_machine_is_sdm658() 0 #define early_machine_is_sda658() 0 #define early_machine_is_sdm630() 0 @@ -206,6 +212,7 @@ enum msm_cpu { MSM_CPU_HAMSTER, MSM_CPU_660, MSM_CPU_630, + MSM_CPU_636, }; struct msm_soc_info { diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index 2680a13721c2..21b30bf7c74c 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -4012,7 +4012,7 @@ struct asm_stream_pan_ctrl_params { uint16_t num_input_channels; uint16_t output_channel_map[8]; uint16_t input_channel_map[8]; - uint16_t gain[64]; + uint32_t gain[64]; } __packed; #define ASM_END_POINT_DEVICE_MATRIX 0 diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h index 177c2f4da32e..9ddd02cac9ac 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -99,7 +99,7 @@ #define SOFT_PAUSE_ENABLE 1 #define SOFT_PAUSE_DISABLE 0 -#define ASM_ACTIVE_STREAMS_ALLOWED 0x8 +#define ASM_ACTIVE_STREAMS_ALLOWED 0x9 /* Control session is used for mapping calibration memory */ #define ASM_CONTROL_SESSION (ASM_ACTIVE_STREAMS_ALLOWED + 1) @@ -639,7 +639,8 @@ int q6asm_send_audio_effects_params(struct audio_client *ac, char *params, int q6asm_send_stream_cmd(struct audio_client *ac, struct msm_adsp_event_data *data); -int q6asm_send_ion_fd(struct audio_client *ac, int fd); +int q6asm_audio_map_shm_fd(struct audio_client *ac, struct ion_client **client, + struct ion_handle **handle, int fd); int q6asm_send_rtic_event_ack(struct audio_client *ac, void *param, uint32_t params_length); diff --git a/include/sound/q6core.h b/include/sound/q6core.h index 1d81bda4b513..4f55880d410f 100644 --- a/include/sound/q6core.h +++ b/include/sound/q6core.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2016, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -20,6 +20,8 @@ #define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D bool q6core_is_adsp_ready(void); +int q6core_add_remove_pool_pages(phys_addr_t buf_add, uint32_t bufsz, + uint32_t mempool_id, bool add_pages); #define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919 #define DTS_EAGLE_LICENSE_ID 0x00028346 @@ -153,4 +155,16 @@ struct avcs_cmd_deregister_topologies { int32_t core_set_license(uint32_t key, uint32_t module_id); int32_t core_get_license_status(uint32_t module_id); +#define ADSP_MEMORY_MAP_HLOS_PHYSPOOL 4 +#define AVCS_CMD_ADD_POOL_PAGES 0x0001292E +#define AVCS_CMD_REMOVE_POOL_PAGES 0x0001292F + +struct avs_mem_assign_region { + struct apr_hdr hdr; + u32 pool_id; + u32 size; + u32 addr_lsw; + u32 addr_msw; +} __packed; + #endif /* __Q6CORE_H__ */ diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h index 373d3342002b..e0efe3fcf739 100644 --- a/include/target/iscsi/iscsi_target_core.h +++ b/include/target/iscsi/iscsi_target_core.h @@ -64,6 +64,14 @@ #define TA_DEFAULT_FABRIC_PROT_TYPE 0 /* TPG status needs to be enabled to return sendtargets discovery endpoint info */ #define TA_DEFAULT_TPG_ENABLED_SENDTARGETS 1 +/* + * Used to control the sending of keys with optional to respond state bit, + * as a workaround for non RFC compliant initiators,that do not propose, + * nor respond to specific keys required for login to complete. + * + * See iscsi_check_proposer_for_optional_reply() for more details. + */ +#define TA_DEFAULT_LOGIN_KEYS_WORKAROUND 1 #define ISCSI_IOV_DATA_BUFFER 5 @@ -765,6 +773,7 @@ struct iscsi_tpg_attrib { u8 t10_pi; u32 fabric_prot_type; u32 tpg_enabled_sendtargets; + u32 login_keys_workaround; struct iscsi_portal_group *tpg; }; diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 84f7f2139a91..feffec7b489a 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -1698,6 +1698,48 @@ TRACE_EVENT(sched_boost_task, ); /* + * Tracepoint for find_best_target + */ +TRACE_EVENT(sched_find_best_target, + + TP_PROTO(struct task_struct *tsk, bool prefer_idle, + unsigned long min_util, int start_cpu, + int best_idle, int best_active, int target), + + TP_ARGS(tsk, prefer_idle, min_util, start_cpu, + best_idle, best_active, target), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( unsigned long, min_util ) + __field( bool, prefer_idle ) + __field( int, start_cpu ) + __field( int, best_idle ) + __field( int, best_active ) + __field( int, target ) + ), + + TP_fast_assign( + memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); + __entry->pid = tsk->pid; + __entry->min_util = min_util; + __entry->prefer_idle = prefer_idle; + __entry->start_cpu = start_cpu; + __entry->best_idle = best_idle; + __entry->best_active = best_active; + __entry->target = target; + ), + + TP_printk("pid=%d comm=%s prefer_idle=%d start_cpu=%d " + "best_idle=%d best_active=%d target=%d", + __entry->pid, __entry->comm, + __entry->prefer_idle, __entry->start_cpu, + __entry->best_idle, __entry->best_active, + __entry->target) +); + +/* * Tracepoint for accounting sched group energy */ TRACE_EVENT(sched_energy_diff, diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 927c3626edb7..a852f2a3701f 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -84,6 +84,21 @@ struct drm_msm_ext_panel_hdr_metadata { }; /** + * HDR Control + * This encapsulates the HDR metadata as well as a state control + * for the HDR metadata as required by the HDMI spec to send the + * relevant metadata depending on the state of the HDR playback. + * hdr_state: Controls HDR state, takes values ENABLE(1)/DISABLE(0) + * hdr_meta: Metadata sent by the userspace for the HDR clip + */ + +#define DRM_MSM_EXT_PANEL_HDR_CTRL +struct drm_msm_ext_panel_hdr_ctrl { + __u8 hdr_state; /* HDR state */ + struct drm_msm_ext_panel_hdr_metadata hdr_meta; /* HDR metadata */ +}; + +/** * HDR sink properties * These are defined as per EDID spec and shall be used by the userspace * to determine the HDR properties to be set to the sink. @@ -219,7 +234,7 @@ struct drm_msm_gem_submit_cmd { __u32 size; /* in, cmdstream size */ __u32 pad; __u32 nr_relocs; /* in, number of submit_reloc's */ - __u64 __user relocs; /* in, ptr to array of submit_reloc's */ + __u64 relocs; /* in, ptr to array of submit_reloc's */ }; /* Each buffer referenced elsewhere in the cmdstream submit (ie. the @@ -259,8 +274,8 @@ struct drm_msm_gem_submit { __u32 fence; /* out */ __u32 nr_bos; /* in, number of submit_bo's */ __u32 nr_cmds; /* in, number of submit_cmd's */ - __u64 __user bos; /* in, ptr to array of submit_bo's */ - __u64 __user cmds; /* in, ptr to array of submit_cmd's */ + __u64 bos; /* in, ptr to array of submit_bo's */ + __u64 cmds; /* in, ptr to array of submit_cmd's */ __s32 fence_fd; /* gap for the fence_fd which is upstream */ __u32 queueid; /* in, submitqueue id */ }; diff --git a/include/uapi/drm/sde_drm.h b/include/uapi/drm/sde_drm.h index bef841446247..71159cb377d8 100644 --- a/include/uapi/drm/sde_drm.h +++ b/include/uapi/drm/sde_drm.h @@ -337,4 +337,14 @@ struct sde_drm_wb_cfg { uint64_t modes; }; +/** + * Define extended power modes supported by the SDE connectors. + */ +#define SDE_MODE_DPMS_ON 0 +#define SDE_MODE_DPMS_LP1 1 +#define SDE_MODE_DPMS_LP2 2 +#define SDE_MODE_DPMS_STANDBY 3 +#define SDE_MODE_DPMS_SUSPEND 4 +#define SDE_MODE_DPMS_OFF 5 + #endif /* _SDE_DRM_H_ */ diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 7668b5791c91..5539933b3491 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -37,9 +37,56 @@ enum { BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE), }; -enum { +/** + * enum flat_binder_object_shifts: shift values for flat_binder_object_flags + * @FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT: shift for getting scheduler policy. + * + */ +enum flat_binder_object_shifts { + FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT = 9, +}; + +/** + * enum flat_binder_object_flags - flags for use in flat_binder_object.flags + */ +enum flat_binder_object_flags { + /** + * @FLAT_BINDER_FLAG_PRIORITY_MASK: bit-mask for min scheduler priority + * + * These bits can be used to set the minimum scheduler priority + * at which transactions into this node should run. Valid values + * in these bits depend on the scheduler policy encoded in + * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK. + * + * For SCHED_NORMAL/SCHED_BATCH, the valid range is between [-20..19] + * For SCHED_FIFO/SCHED_RR, the value can run between [1..99] + */ FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, + /** + * @FLAT_BINDER_FLAG_ACCEPTS_FDS: whether the node accepts fds. + */ FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, + /** + * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK: bit-mask for scheduling policy + * + * These two bits can be used to set the min scheduling policy at which + * transactions on this node should run. These match the UAPI + * scheduler policy values, eg: + * 00b: SCHED_NORMAL + * 01b: SCHED_FIFO + * 10b: SCHED_RR + * 11b: SCHED_BATCH + */ + FLAT_BINDER_FLAG_SCHED_POLICY_MASK = + 3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT, + + /** + * @FLAT_BINDER_FLAG_INHERIT_RT: whether the node inherits RT policy + * + * Only when set, calls into this node will inherit a real-time + * scheduling policy from the caller (for synchronous transactions). + */ + FLAT_BINDER_FLAG_INHERIT_RT = 0x800, }; #ifdef BINDER_IPC_32BIT @@ -186,6 +233,19 @@ struct binder_version { #define BINDER_CURRENT_PROTOCOL_VERSION 8 #endif +/* + * Use with BINDER_GET_NODE_DEBUG_INFO, driver reads ptr, writes to all fields. + * Set ptr to NULL for the first call to get the info for the first node, and + * then repeat the call passing the previously returned value to get the next + * nodes. ptr will be 0 when there are no more nodes. + */ +struct binder_node_debug_info { + binder_uintptr_t ptr; + binder_uintptr_t cookie; + __u32 has_strong_ref; + __u32 has_weak_ref; +}; + #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) @@ -193,6 +253,7 @@ struct binder_version { #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32) #define BINDER_THREAD_EXIT _IOW('b', 8, __s32) #define BINDER_VERSION _IOWR('b', 9, struct binder_version) +#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) /* * NOTE: Two special error codes you should check for when calling diff --git a/include/uapi/linux/ipa_qmi_service_v01.h b/include/uapi/linux/ipa_qmi_service_v01.h index 60867630e1a1..dc46ee0f29a2 100644 --- a/include/uapi/linux/ipa_qmi_service_v01.h +++ b/include/uapi/linux/ipa_qmi_service_v01.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -47,6 +47,12 @@ #define QMI_IPA_MAX_FILTERS_EX_V01 128 #define QMI_IPA_MAX_PIPES_V01 20 #define QMI_IPA_MAX_APN_V01 8 +#define QMI_IPA_MAX_PER_CLIENTS_V01 64 +/* Currently max we can use is only 1. But for scalability purpose + * we are having max value as 8. + */ +#define QMI_IPA_MAX_CLIENT_DST_PIPES_V01 8 +#define QMI_IPA_MAX_UL_FIREWALL_RULES_V01 64 #define IPA_INT_MAX ((int)(~0U>>1)) #define IPA_INT_MIN (-IPA_INT_MAX - 1) @@ -984,6 +990,16 @@ struct ipa_fltr_installed_notif_req_msg_v01 { * failure, the Rule Ids in this list must be set to a reserved * index (255). */ + + /* Optional */ + /* List of destination pipe IDs. */ + uint8_t dst_pipe_id_valid; + /* Must be set to true if dst_pipe_id is being passed. */ + uint32_t dst_pipe_id_len; + /* Must be set to # of elements in dst_pipe_id. */ + uint32_t dst_pipe_id[QMI_IPA_MAX_CLIENT_DST_PIPES_V01]; + /* Provides the list of destination pipe IDs for a source pipe. */ + }; /* Message */ /* Response Message; This is the message that is exchanged between the @@ -1622,6 +1638,273 @@ struct ipa_install_fltr_rule_resp_ex_msg_v01 { */ }; /* Message */ +/* + * Request Message; Requests the modem IPA driver to enable or + * disable collection of per client statistics. + */ +struct ipa_enable_per_client_stats_req_msg_v01 { + + /* Mandatory */ + /* Collect statistics per client; */ + uint8_t enable_per_client_stats; + /* + * Indicates whether to start or stop collecting + * per client statistics. + */ +}; /* Message */ + +/* + * Response Message; Requests the modem IPA driver to enable or disable + * collection of per client statistics. + */ +struct ipa_enable_per_client_stats_resp_msg_v01 { + + /* Mandatory */ + /* Result Code */ + struct ipa_qmi_response_type_v01 resp; + /* Standard response type. */ +}; /* Message */ + +struct ipa_per_client_stats_info_type_v01 { + + uint32_t client_id; + /* + * Id of the client on APPS processor side for which Modem processor + * needs to send uplink/downlink statistics. + */ + + uint32_t src_pipe_id; + /* + * IPA consumer pipe on which client on APPS side sent uplink + * data to modem. + */ + + uint64_t num_ul_ipv4_bytes; + /* + * Accumulated number of uplink IPv4 bytes for a client. + */ + + uint64_t num_ul_ipv6_bytes; + /* + * Accumulated number of uplink IPv6 bytes for a client. + */ + + uint64_t num_dl_ipv4_bytes; + /* + * Accumulated number of downlink IPv4 bytes for a client. + */ + + uint64_t num_dl_ipv6_bytes; + /* + * Accumulated number of downlink IPv6 byes for a client. + */ + + + uint32_t num_ul_ipv4_pkts; + /* + * Accumulated number of uplink IPv4 packets for a client. + */ + + uint32_t num_ul_ipv6_pkts; + /* + * Accumulated number of uplink IPv6 packets for a client. + */ + + uint32_t num_dl_ipv4_pkts; + /* + * Accumulated number of downlink IPv4 packets for a client. + */ + + uint32_t num_dl_ipv6_pkts; + /* + * Accumulated number of downlink IPv6 packets for a client. + */ +}; /* Type */ + +/* + * Request Message; Requests the modem IPA driver to provide statistics + * for a givenclient. + */ +struct ipa_get_stats_per_client_req_msg_v01 { + + /* Mandatory */ + /* Client id */ + uint32_t client_id; + /* + * Id of the client on APPS processor side for which Modem processor + * needs to send uplink/downlink statistics. if client id is specified + * as 0xffffffff, then Q6 will send the stats for all the clients of + * the specified source pipe. + */ + + /* Mandatory */ + /* Source pipe id */ + uint32_t src_pipe_id; + /* + * IPA consumer pipe on which client on APPS side sent uplink + * data to modem. In future, this implementation can be extended + * to provide 0xffffffff as the source pipe id, where Q6 will send + * the stats of all the clients across all different tethered-pipes. + */ + + /* Optional */ + /* Reset client statistics. */ + uint8_t reset_stats_valid; + /* Must be set to true if reset_stats is being passed. */ + uint8_t reset_stats; + /* + * Option to reset the statistics currently collected by modem for this + * particular client. + */ +}; /* Message */ + +/* + * Response Message; Requests the modem IPA driver to provide statistics + * for a given client. + */ +struct ipa_get_stats_per_client_resp_msg_v01 { + + /* Mandatory */ + /* Result Code */ + struct ipa_qmi_response_type_v01 resp; + /* Standard response type. */ + + /* Optional */ + /* Per clients Statistics List */ + uint8_t per_client_stats_list_valid; + /* Must be set to true if per_client_stats_list is being passed. */ + uint32_t per_client_stats_list_len; + /* Must be set to # of elements in per_client_stats_list. */ + struct ipa_per_client_stats_info_type_v01 + per_client_stats_list[QMI_IPA_MAX_PER_CLIENTS_V01]; + /* + * List of all per client statistics that are retrieved. + */ +}; /* Message */ + +struct ipa_ul_firewall_rule_type_v01 { + + enum ipa_ip_type_enum_v01 ip_type; + /* + * IP type for which this rule is applicable. + * The driver must identify the filter table (v6 or v4), and this + * field is essential for that. Values: + * - QMI_IPA_IP_TYPE_INVALID (0) -- Invalid IP type identifier + * - QMI_IPA_IP_TYPE_V4 (1) -- IPv4 type + * - QMI_IPA_IP_TYPE_V6 (2) -- IPv6 type + */ + + struct ipa_filter_rule_type_v01 filter_rule; + /* + * Rules in the filter specification. These rules are the + * ones that are matched against fields in the packet. + * Currently we only send IPv6 whitelist rules to Q6. + */ +}; /* Type */ + +/* + * Request Message; Requestes remote IPA driver to install uplink + * firewall rules. + */ +struct ipa_configure_ul_firewall_rules_req_msg_v01 { + + /* Optional */ + /* Uplink Firewall Specification */ + uint32_t firewall_rules_list_len; + /* Must be set to # of elements in firewall_rules_list. */ + struct ipa_ul_firewall_rule_type_v01 + firewall_rules_list[QMI_IPA_MAX_UL_FIREWALL_RULES_V01]; + /* + * List of uplink firewall specifications of filters that must be + * installed. + */ + + uint32_t mux_id; + /* + * QMAP Mux ID. As a part of the QMAP protocol, + * several data calls may be multiplexed over the same physical + * transport channel. This identifier is used to identify one + * such data call. The maximum value for this identifier is 255. + */ + + /* Optional */ + uint8_t disable_valid; + /* Must be set to true if enable is being passed. */ + uint8_t disable; + /* + * Indicates whether uplink firewall needs to be enabled or disabled. + */ + + /* Optional */ + uint8_t are_blacklist_filters_valid; + /* Must be set to true if are_blacklist_filters is being passed. */ + uint8_t are_blacklist_filters; + /* + * Indicates whether the filters received as part of this message are + * blacklist filters. i.e. drop uplink packets matching these rules. + */ +}; /* Message */ + +/* + * Response Message; Requestes remote IPA driver to install + * uplink firewall rules. + */ +struct ipa_configure_ul_firewall_rules_resp_msg_v01 { + + /* Mandatory */ + /* Result Code */ + struct ipa_qmi_response_type_v01 resp; + /* + * Standard response type. + * Standard response type. Contains the following data members: + * qmi_result_type -- QMI_RESULT_SUCCESS or QMI_RESULT_FAILURE + * qmi_error_type -- Error code. Possible error code values are + * described in the error codes section of each message definition. + */ +}; /* Message */ + +enum ipa_ul_firewall_status_enum_v01 { + IPA_UL_FIREWALL_STATUS_ENUM_MIN_ENUM_VAL_V01 = -2147483647, + /* To force a 32 bit signed enum. Do not change or use*/ + QMI_IPA_UL_FIREWALL_STATUS_SUCCESS_V01 = 0, + /* Indicates that the uplink firewall rules + * are configured successfully. + */ + QMI_IPA_UL_FIREWALL_STATUS_FAILURE_V01 = 1, + /* Indicates that the uplink firewall rules + * are not configured successfully. + */ + IPA_UL_FIREWALL_STATUS_ENUM_MAX_ENUM_VAL_V01 = 2147483647 + /* To force a 32 bit signed enum. Do not change or use*/ +}; + +struct ipa_ul_firewall_config_result_type_v01 { + + enum ipa_ul_firewall_status_enum_v01 is_success; + /* + * Indicates whether the uplink firewall rules are configured + * successfully. + */ + + uint32_t mux_id; + /* + * QMAP Mux ID. As a part of the QMAP protocol, + * several data calls may be multiplexed over the same physical + * transport channel. This identifier is used to identify one + * such data call. The maximum value for this identifier is 255. + */ +}; + +/* + * Indication Message; Requestes remote IPA driver to install + * uplink firewall rules. + */ +struct ipa_configure_ul_firewall_rules_ind_msg_v01 { + + struct ipa_ul_firewall_config_result_type_v01 result; +}; /* Message */ + + /*Service Message Definition*/ #define QMI_IPA_INDICATION_REGISTER_REQ_V01 0x0020 #define QMI_IPA_INDICATION_REGISTER_RESP_V01 0x0020 @@ -1655,6 +1938,13 @@ struct ipa_install_fltr_rule_resp_ex_msg_v01 { #define QMI_IPA_INIT_MODEM_DRIVER_CMPLT_RESP_V01 0x0035 #define QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_V01 0x0037 #define QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_V01 0x0037 +#define QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_V01 0x0038 +#define QMI_IPA_ENABLE_PER_CLIENT_STATS_RESP_V01 0x0038 +#define QMI_IPA_GET_STATS_PER_CLIENT_REQ_V01 0x0039 +#define QMI_IPA_GET_STATS_PER_CLIENT_RESP_V01 0x0039 +#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_V01 0x003A +#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_RESP_V01 0x003A +#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_V01 0x003A /* add for max length*/ #define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 134 @@ -1663,7 +1953,7 @@ struct ipa_install_fltr_rule_resp_ex_msg_v01 { #define QMI_IPA_INDICATION_REGISTER_RESP_MAX_MSG_LEN_V01 7 #define QMI_IPA_INSTALL_FILTER_RULE_REQ_MAX_MSG_LEN_V01 22369 #define QMI_IPA_INSTALL_FILTER_RULE_RESP_MAX_MSG_LEN_V01 783 -#define QMI_IPA_FILTER_INSTALLED_NOTIF_REQ_MAX_MSG_LEN_V01 834 +#define QMI_IPA_FILTER_INSTALLED_NOTIF_REQ_MAX_MSG_LEN_V01 870 #define QMI_IPA_FILTER_INSTALLED_NOTIF_RESP_MAX_MSG_LEN_V01 7 #define QMI_IPA_MASTER_DRIVER_INIT_COMPLETE_IND_MAX_MSG_LEN_V01 7 #define QMI_IPA_DATA_USAGE_QUOTA_REACHED_IND_MAX_MSG_LEN_V01 15 @@ -1696,6 +1986,15 @@ struct ipa_install_fltr_rule_resp_ex_msg_v01 { #define QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_MAX_MSG_LEN_V01 22685 #define QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_MAX_MSG_LEN_V01 523 +#define QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_MAX_MSG_LEN_V01 4 +#define QMI_IPA_ENABLE_PER_CLIENT_STATS_RESP_MAX_MSG_LEN_V01 7 + +#define QMI_IPA_GET_STATS_PER_CLIENT_REQ_MAX_MSG_LEN_V01 18 +#define QMI_IPA_GET_STATS_PER_CLIENT_RESP_MAX_MSG_LEN_V01 3595 + +#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_MAX_MSG_LEN_V01 9875 +#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_RESP_MAX_MSG_LEN_V01 7 +#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_MAX_MSG_LEN_V01 11 /* Service Object Accessor */ #endif/* IPA_QMI_SERVICE_V01_H */ diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h index 83927c614e91..a92c144f712e 100644 --- a/include/uapi/media/msm_camsensor_sdk.h +++ b/include/uapi/media/msm_camsensor_sdk.h @@ -113,8 +113,10 @@ enum msm_sensor_power_seq_gpio_t { SENSOR_GPIO_FL_RESET, SENSOR_GPIO_CUSTOM1, SENSOR_GPIO_CUSTOM2, + SENSOR_GPIO_CUSTOM3, SENSOR_GPIO_MAX, }; +#define SENSOR_GPIO_CUSTOM3 SENSOR_GPIO_CUSTOM3 enum msm_ir_cut_filter_gpio_t { IR_CUT_FILTER_GPIO_P = 0, diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 866ec3d2af69..7845fdd556fa 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -138,6 +138,11 @@ struct snd_compr_audio_info { #define SNDRV_COMPRESS_CLK_REC_MODE_NONE 0 #define SNDRV_COMPRESS_CLK_REC_MODE_AUTO 1 +enum sndrv_compress_latency_mode { + SNDRV_COMPRESS_LEGACY_LATENCY_MODE = 0, + SNDRV_COMPRESS_LOW_LATENCY_MODE = 1, +}; + /** * enum sndrv_compress_encoder * @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the @@ -164,6 +169,7 @@ enum sndrv_compress_encoder { SNDRV_COMPRESS_START_DELAY = 9, SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK = 10, SNDRV_COMPRESS_ADJUST_SESSION_CLOCK = 11, + SNDRV_COMPRESS_LATENCY_MODE = 12, }; #define SNDRV_COMPRESS_PATH_DELAY SNDRV_COMPRESS_PATH_DELAY @@ -174,6 +180,7 @@ enum sndrv_compress_encoder { #define SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK \ SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK #define SNDRV_COMPRESS_ADJUST_SESSION_CLOCK SNDRV_COMPRESS_ADJUST_SESSION_CLOCK +#define SNDRV_COMPRESS_LATENCY_MODE SNDRV_COMPRESS_LATENCY_MODE /** * struct snd_compr_metadata - compressed stream metadata diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 25b7a678f9ef..46436543ad28 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -1251,8 +1251,10 @@ retry: timeo = MAX_SCHEDULE_TIMEOUT; ret = netlink_attachskb(sock, nc, &timeo, NULL); - if (ret == 1) + if (ret == 1) { + sock = NULL; goto retry; + } if (ret) { sock = NULL; nc = NULL; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 85de5094b936..c97bce6a0e0e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -765,6 +765,11 @@ static int check_xadd(struct verifier_env *env, struct bpf_insn *insn) if (err) return err; + if (is_pointer_value(env, insn->src_reg)) { + verbose("R%d leaks addr into mem\n", insn->src_reg); + return -EACCES; + } + /* check whether atomic_add can read the memory */ err = check_mem_access(env, insn->dst_reg, insn->off, BPF_SIZE(insn->code), BPF_READ, -1); diff --git a/kernel/cpu.c b/kernel/cpu.c index 1a26ef5b7d58..1d6b0a209bc0 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -375,6 +375,21 @@ static int _cpu_down(unsigned int cpu, int tasks_frozen) goto out_release; } + /* + * By now we've cleared cpu_active_mask, wait for all preempt-disabled + * and RCU users of this state to go away such that all new such users + * will observe it. + * + * For CONFIG_PREEMPT we have preemptible RCU and its sync_rcu() might + * not imply sync_sched(), so wait for both. + * + * Do sync before park smpboot threads to take care the rcu boost case. + */ + if (IS_ENABLED(CONFIG_PREEMPT)) + synchronize_rcu_mult(call_rcu, call_rcu_sched); + else + synchronize_rcu(); + smpboot_park_threads(cpu); /* @@ -508,8 +523,8 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen) ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls); if (ret) { nr_calls--; - pr_warn("%s: attempt to bring up CPU %u failed\n", - __func__, cpu); + pr_warn_ratelimited("%s: attempt to bring up CPU %u failed\n", + __func__, cpu); goto out_notify; } diff --git a/kernel/events/core.c b/kernel/events/core.c index 7fee87daac56..87f5d841f796 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1693,7 +1693,33 @@ static int __perf_remove_from_context(void *info) return 0; } -/* + +#ifdef CONFIG_SMP +static void perf_retry_remove(struct perf_event *event, + struct remove_event *rep) +{ + int up_ret; + /* + * CPU was offline. Bring it online so we can + * gracefully exit a perf context. + */ + up_ret = cpu_up(event->cpu); + if (!up_ret) + /* Try the remove call once again. */ + cpu_function_call(event->cpu, __perf_remove_from_context, + rep); + else + pr_err("Failed to bring up CPU: %d, ret: %d\n", + event->cpu, up_ret); +} +#else +static void perf_retry_remove(struct perf_event *event, + struct remove_event *rep) +{ +} +#endif + + /* * Remove the event from a task's (or a CPU's) list of events. * * CPU events are removed with a smp call. For task events we only @@ -1728,6 +1754,9 @@ static void __ref perf_remove_from_context(struct perf_event *event, */ ret = cpu_function_call(event->cpu, __perf_remove_from_context, &re); + if (ret == -ENXIO) + perf_retry_remove(event, &re); + return; } @@ -3408,22 +3437,27 @@ u64 perf_event_read_local(struct perf_event *event) static int perf_event_read(struct perf_event *event, bool group) { - int ret = 0; + int event_cpu, ret = 0; /* * If event is enabled and currently active on a CPU, update the * value in the event structure: */ + event_cpu = READ_ONCE(event->oncpu); + if (event->state == PERF_EVENT_STATE_ACTIVE && - !cpu_isolated(event->oncpu)) { + !cpu_isolated(event_cpu)) { struct perf_read_data data = { .event = event, .group = group, .ret = 0, }; + + if ((unsigned int)event_cpu >= nr_cpu_ids) + return 0; if (!event->attr.exclude_idle || - !per_cpu(is_idle, event->oncpu)) { - smp_call_function_single(event->oncpu, + !per_cpu(is_idle, event_cpu)) { + smp_call_function_single(event_cpu, __perf_event_read, &data, 1); ret = data.ret; } @@ -6566,21 +6600,6 @@ static void perf_log_itrace_start(struct perf_event *event) perf_output_end(&handle); } -static bool sample_is_allowed(struct perf_event *event, struct pt_regs *regs) -{ - /* - * Due to interrupt latency (AKA "skid"), we may enter the - * kernel before taking an overflow, even if the PMU is only - * counting user events. - * To avoid leaking information to userspace, we must always - * reject kernel samples when exclude_kernel is set. - */ - if (event->attr.exclude_kernel && !user_mode(regs)) - return false; - - return true; -} - /* * Generic event overflow handling, sampling. */ @@ -6628,12 +6647,6 @@ static int __perf_event_overflow(struct perf_event *event, } /* - * For security, drop the skid kernel samples if necessary. - */ - if (!sample_is_allowed(event, regs)) - return ret; - - /* * XXX event_limit might not quite work as expected on inherited * events */ @@ -7109,6 +7122,8 @@ static struct pmu perf_swevent = { .start = perf_swevent_start, .stop = perf_swevent_stop, .read = perf_swevent_read, + + .events_across_hotplug = 1, }; #ifdef CONFIG_EVENT_TRACING @@ -7230,6 +7245,8 @@ static struct pmu perf_tracepoint = { .start = perf_swevent_start, .stop = perf_swevent_stop, .read = perf_swevent_read, + + .events_across_hotplug = 1, }; static inline void perf_tp_register(void) @@ -7517,6 +7534,8 @@ static struct pmu perf_cpu_clock = { .start = cpu_clock_event_start, .stop = cpu_clock_event_stop, .read = cpu_clock_event_read, + + .events_across_hotplug = 1, }; /* @@ -7598,6 +7617,8 @@ static struct pmu perf_task_clock = { .start = task_clock_event_start, .stop = task_clock_event_stop, .read = task_clock_event_read, + + .events_across_hotplug = 1, }; static void perf_pmu_nop_void(struct pmu *pmu) diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index 92ce5f4ccc26..7da5b674d16e 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -614,6 +614,8 @@ static struct pmu perf_breakpoint = { .start = hw_breakpoint_start, .stop = hw_breakpoint_stop, .read = hw_breakpoint_pmu_read, + + .events_across_hotplug = 1, }; int __init init_hw_breakpoint(void) diff --git a/kernel/extable.c b/kernel/extable.c index e820ccee9846..4f06fc34313f 100644 --- a/kernel/extable.c +++ b/kernel/extable.c @@ -66,7 +66,7 @@ static inline int init_kernel_text(unsigned long addr) return 0; } -int core_kernel_text(unsigned long addr) +int notrace core_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_stext && addr < (unsigned long)_etext) diff --git a/kernel/fork.c b/kernel/fork.c index 246b8a57a32d..fef4df444f47 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -832,8 +832,7 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) mm = get_task_mm(task); if (mm && mm != current->mm && - !ptrace_may_access(task, mode) && - !capable(CAP_SYS_RESOURCE)) { + !ptrace_may_access(task, mode)) { mmput(mm); mm = ERR_PTR(-EACCES); } diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c index a4d4de05b2d1..75c950ede9c7 100644 --- a/kernel/locking/rwsem-xadd.c +++ b/kernel/locking/rwsem-xadd.c @@ -511,6 +511,41 @@ struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem) unsigned long flags; /* + * If a spinner is present, there is a chance that the load of + * rwsem_has_spinner() in rwsem_wake() can be reordered with + * respect to decrement of rwsem count in __up_write() leading + * to wakeup being missed. + * + * spinning writer up_write caller + * --------------- ----------------------- + * [S] osq_unlock() [L] osq + * spin_lock(wait_lock) + * sem->count=0xFFFFFFFF00000001 + * +0xFFFFFFFF00000000 + * count=sem->count + * MB + * sem->count=0xFFFFFFFE00000001 + * -0xFFFFFFFF00000001 + * RMB + * spin_trylock(wait_lock) + * return + * rwsem_try_write_lock(count) + * spin_unlock(wait_lock) + * schedule() + * + * Reordering of atomic_long_sub_return_release() in __up_write() + * and rwsem_has_spinner() in rwsem_wake() can cause missing of + * wakeup in up_write() context. In spinning writer, sem->count + * and local variable count is 0XFFFFFFFE00000001. It would result + * in rwsem_try_write_lock() failing to acquire rwsem and spinning + * writer going to sleep in rwsem_down_write_failed(). + * + * The smp_rmb() here is to make sure that the spinner state is + * consulted after sem->count is updated in up_write context. + */ + smp_rmb(); + + /* * If a spinner is present, it is not necessary to do the wakeup. * Try to do wakeup only if the trylock succeeds to minimize * spinlock contention which may introduce too much delay in the diff --git a/kernel/resource.c b/kernel/resource.c index 4c9835c09dcd..c09d484f7b5f 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -105,16 +105,25 @@ static int r_show(struct seq_file *m, void *v) { struct resource *root = m->private; struct resource *r = v, *p; + unsigned long long start, end; int width = root->end < 0x10000 ? 4 : 8; int depth; for (depth = 0, p = r; depth < MAX_IORES_LEVEL; depth++, p = p->parent) if (p->parent == root) break; + + if (file_ns_capable(m->file, &init_user_ns, CAP_SYS_ADMIN)) { + start = r->start; + end = r->end; + } else { + start = end = 0; + } + seq_printf(m, "%*s%0*llx-%0*llx : %s\n", depth * 2, "", - width, (unsigned long long) r->start, - width, (unsigned long long) r->end, + width, start, + width, end, r->name ? r->name : "<BAD>"); return 0; } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 0071785e698b..4ecca604e64b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6924,6 +6924,9 @@ enum s_alloc { * Build an iteration mask that can exclude certain CPUs from the upwards * domain traversal. * + * Only CPUs that can arrive at this group should be considered to continue + * balancing. + * * Asymmetric node setups can result in situations where the domain tree is of * unequal depth, make sure to skip domains that already cover the entire * range. @@ -6935,18 +6938,31 @@ enum s_alloc { */ static void build_group_mask(struct sched_domain *sd, struct sched_group *sg) { - const struct cpumask *span = sched_domain_span(sd); + const struct cpumask *sg_span = sched_group_cpus(sg); struct sd_data *sdd = sd->private; struct sched_domain *sibling; int i; - for_each_cpu(i, span) { + for_each_cpu(i, sg_span) { sibling = *per_cpu_ptr(sdd->sd, i); - if (!cpumask_test_cpu(i, sched_domain_span(sibling))) + + /* + * Can happen in the asymmetric case, where these siblings are + * unused. The mask will not be empty because those CPUs that + * do have the top domain _should_ span the domain. + */ + if (!sibling->child) + continue; + + /* If we would not end up here, we can't continue from here */ + if (!cpumask_equal(sg_span, sched_domain_span(sibling->child))) continue; cpumask_set_cpu(i, sched_group_mask(sg)); } + + /* We must not have empty masks here */ + WARN_ON_ONCE(cpumask_empty(sched_group_mask(sg))); } /* @@ -9216,11 +9232,20 @@ cpu_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) if (IS_ERR(tg)) return ERR_PTR(-ENOMEM); - sched_online_group(tg, parent); - return &tg->css; } +/* Expose task group only after completing cgroup initialization */ +static int cpu_cgroup_css_online(struct cgroup_subsys_state *css) +{ + struct task_group *tg = css_tg(css); + struct task_group *parent = css_tg(css->parent); + + if (parent) + sched_online_group(tg, parent); + return 0; +} + static void cpu_cgroup_css_released(struct cgroup_subsys_state *css) { struct task_group *tg = css_tg(css); @@ -9602,6 +9627,7 @@ static struct cftype cpu_files[] = { struct cgroup_subsys cpu_cgrp_subsys = { .css_alloc = cpu_cgroup_css_alloc, + .css_online = cpu_cgroup_css_online, .css_released = cpu_cgroup_css_released, .css_free = cpu_cgroup_css_free, .fork = cpu_cgroup_fork, diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 75bfbb336722..e12309c1b07b 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -47,6 +47,7 @@ struct sugov_policy { s64 up_rate_delay_ns; s64 down_rate_delay_ns; unsigned int next_freq; + unsigned int cached_raw_freq; /* The next fields are only needed if fast switch cannot be used. */ struct irq_work irq_work; @@ -63,7 +64,6 @@ struct sugov_cpu { struct update_util_data update_util; struct sugov_policy *sg_policy; - unsigned int cached_raw_freq; unsigned long iowait_boost; unsigned long iowait_boost_max; u64 last_update; @@ -72,6 +72,11 @@ struct sugov_cpu { unsigned long util; unsigned long max; unsigned int flags; + + /* The field below is for single-CPU policies only. */ +#ifdef CONFIG_NO_HZ_COMMON + unsigned long saved_idle_calls; +#endif }; static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu); @@ -127,22 +132,20 @@ static void sugov_update_commit(struct sugov_policy *sg_policy, u64 time, if (sugov_up_down_rate_limit(sg_policy, time, next_freq)) return; + if (sg_policy->next_freq == next_freq) + return; + + sg_policy->next_freq = next_freq; + sg_policy->last_freq_update_time = time; + if (policy->fast_switch_enabled) { - if (sg_policy->next_freq == next_freq) { - trace_cpu_frequency(policy->cur, smp_processor_id()); - return; - } - sg_policy->next_freq = next_freq; - sg_policy->last_freq_update_time = time; next_freq = cpufreq_driver_fast_switch(policy, next_freq); if (next_freq == CPUFREQ_ENTRY_INVALID) return; policy->cur = next_freq; trace_cpu_frequency(next_freq, smp_processor_id()); - } else if (sg_policy->next_freq != next_freq) { - sg_policy->next_freq = next_freq; - sg_policy->last_freq_update_time = time; + } else { sg_policy->work_in_progress = true; irq_work_queue(&sg_policy->irq_work); } @@ -150,7 +153,7 @@ static void sugov_update_commit(struct sugov_policy *sg_policy, u64 time, /** * get_next_freq - Compute a new frequency for a given cpufreq policy. - * @sg_cpu: schedutil cpu object to compute the new frequency for. + * @sg_policy: schedutil policy object to compute the new frequency for. * @util: Current CPU utilization. * @max: CPU capacity. * @@ -170,19 +173,18 @@ static void sugov_update_commit(struct sugov_policy *sg_policy, u64 time, * next_freq (as calculated above) is returned, subject to policy min/max and * cpufreq driver limitations. */ -static unsigned int get_next_freq(struct sugov_cpu *sg_cpu, unsigned long util, - unsigned long max) +static unsigned int get_next_freq(struct sugov_policy *sg_policy, + unsigned long util, unsigned long max) { - struct sugov_policy *sg_policy = sg_cpu->sg_policy; struct cpufreq_policy *policy = sg_policy->policy; unsigned int freq = arch_scale_freq_invariant() ? policy->cpuinfo.max_freq : policy->cur; freq = (freq + (freq >> 2)) * util / max; - if (freq == sg_cpu->cached_raw_freq && sg_policy->next_freq != UINT_MAX) + if (freq == sg_policy->cached_raw_freq && sg_policy->next_freq != UINT_MAX) return sg_policy->next_freq; - sg_cpu->cached_raw_freq = freq; + sg_policy->cached_raw_freq = freq; return cpufreq_driver_resolve_freq(policy, freq); } @@ -248,6 +250,19 @@ static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, unsigned long *util, sg_cpu->iowait_boost >>= 1; } +#ifdef CONFIG_NO_HZ_COMMON +static bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu) +{ + unsigned long idle_calls = tick_nohz_get_idle_calls(); + bool ret = idle_calls == sg_cpu->saved_idle_calls; + + sg_cpu->saved_idle_calls = idle_calls; + return ret; +} +#else +static inline bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu) { return false; } +#endif /* CONFIG_NO_HZ_COMMON */ + static void sugov_update_single(struct update_util_data *hook, u64 time, unsigned int flags) { @@ -256,6 +271,7 @@ static void sugov_update_single(struct update_util_data *hook, u64 time, struct cpufreq_policy *policy = sg_policy->policy; unsigned long util, max; unsigned int next_f; + bool busy; sugov_set_iowait_boost(sg_cpu, time, flags); sg_cpu->last_update = time; @@ -263,40 +279,37 @@ static void sugov_update_single(struct update_util_data *hook, u64 time, if (!sugov_should_update_freq(sg_policy, time)) return; + busy = sugov_cpu_is_busy(sg_cpu); + if (flags & SCHED_CPUFREQ_DL) { next_f = policy->cpuinfo.max_freq; } else { sugov_get_util(&util, &max, time); sugov_iowait_boost(sg_cpu, &util, &max); - next_f = get_next_freq(sg_cpu, util, max); + next_f = get_next_freq(sg_policy, util, max); + /* + * Do not reduce the frequency if the CPU has not been idle + * recently, as the reduction is likely to be premature then. + */ + if (busy && next_f < sg_policy->next_freq) + next_f = sg_policy->next_freq; } sugov_update_commit(sg_policy, time, next_f); } -static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu, - unsigned long util, unsigned long max, - unsigned int flags) +static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu) { struct sugov_policy *sg_policy = sg_cpu->sg_policy; struct cpufreq_policy *policy = sg_policy->policy; - unsigned int max_f = policy->cpuinfo.max_freq; u64 last_freq_update_time = sg_policy->last_freq_update_time; + unsigned long util = 0, max = 1; unsigned int j; - if (flags & SCHED_CPUFREQ_DL) - return max_f; - - sugov_iowait_boost(sg_cpu, &util, &max); - for_each_cpu(j, policy->cpus) { - struct sugov_cpu *j_sg_cpu; + struct sugov_cpu *j_sg_cpu = &per_cpu(sugov_cpu, j); unsigned long j_util, j_max; s64 delta_ns; - if (j == smp_processor_id()) - continue; - - j_sg_cpu = &per_cpu(sugov_cpu, j); /* * If the CPU utilization was last updated before the previous * frequency update and the time elapsed between the last update @@ -310,7 +323,7 @@ static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu, continue; } if (j_sg_cpu->flags & SCHED_CPUFREQ_DL) - return max_f; + return policy->cpuinfo.max_freq; j_util = j_sg_cpu->util; j_max = j_sg_cpu->max; @@ -322,7 +335,7 @@ static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu, sugov_iowait_boost(j_sg_cpu, &util, &max); } - return get_next_freq(sg_cpu, util, max); + return get_next_freq(sg_policy, util, max); } static void sugov_update_shared(struct update_util_data *hook, u64 time, @@ -345,7 +358,11 @@ static void sugov_update_shared(struct update_util_data *hook, u64 time, sg_cpu->last_update = time; if (sugov_should_update_freq(sg_policy, time)) { - next_f = sugov_next_freq_shared(sg_cpu, util, max, flags); + if (flags & SCHED_CPUFREQ_DL) + next_f = sg_policy->policy->cpuinfo.max_freq; + else + next_f = sugov_next_freq_shared(sg_cpu); + sugov_update_commit(sg_policy, time, next_f); } @@ -371,15 +388,15 @@ static void sugov_irq_work(struct irq_work *irq_work) sg_policy = container_of(irq_work, struct sugov_policy, irq_work); /* - * For Real Time and Deadline tasks, schedutil governor shoots the - * frequency to maximum. And special care must be taken to ensure that - * this kthread doesn't result in that. + * For RT and deadline tasks, the schedutil governor shoots the + * frequency to maximum. Special care must be taken to ensure that this + * kthread doesn't result in the same behavior. * * This is (mostly) guaranteed by the work_in_progress flag. The flag is - * updated only at the end of the sugov_work() and before that schedutil - * rejects all other frequency scaling requests. + * updated only at the end of the sugov_work() function and before that + * the schedutil governor rejects all other frequency scaling requests. * - * Though there is a very rare case where the RT thread yields right + * There is a very rare case though, where the RT thread yields right * after the work_in_progress flag is cleared. The effects of that are * neglected for now. */ @@ -489,15 +506,12 @@ static struct sugov_policy *sugov_policy_alloc(struct cpufreq_policy *policy) return NULL; sg_policy->policy = policy; - init_irq_work(&sg_policy->irq_work, sugov_irq_work); - mutex_init(&sg_policy->work_lock); raw_spin_lock_init(&sg_policy->update_lock); return sg_policy; } static void sugov_policy_free(struct sugov_policy *sg_policy) { - mutex_destroy(&sg_policy->work_lock); kfree(sg_policy); } @@ -531,6 +545,9 @@ static int sugov_kthread_create(struct sugov_policy *sg_policy) sg_policy->thread = thread; kthread_bind_mask(thread, policy->related_cpus); + init_irq_work(&sg_policy->irq_work, sugov_irq_work); + mutex_init(&sg_policy->work_lock); + wake_up_process(thread); return 0; @@ -544,6 +561,7 @@ static void sugov_kthread_stop(struct sugov_policy *sg_policy) flush_kthread_worker(&sg_policy->worker); kthread_stop(sg_policy->thread); + mutex_destroy(&sg_policy->work_lock); } static struct sugov_tunables *sugov_tunables_alloc(struct sugov_policy *sg_policy) @@ -578,9 +596,13 @@ static int sugov_init(struct cpufreq_policy *policy) if (policy->governor_data) return -EBUSY; + cpufreq_enable_fast_switch(policy); + sg_policy = sugov_policy_alloc(policy); - if (!sg_policy) - return -ENOMEM; + if (!sg_policy) { + ret = -ENOMEM; + goto disable_fast_switch; + } ret = sugov_kthread_create(sg_policy); if (ret) @@ -623,13 +645,11 @@ static int sugov_init(struct cpufreq_policy *policy) if (ret) goto fail; - out: +out: mutex_unlock(&global_tunables_lock); - - cpufreq_enable_fast_switch(policy); return 0; - fail: +fail: policy->governor_data = NULL; sugov_tunables_free(tunables); @@ -640,6 +660,10 @@ free_sg_policy: mutex_unlock(&global_tunables_lock); sugov_policy_free(sg_policy); + +disable_fast_switch: + cpufreq_disable_fast_switch(policy); + pr_err("initialization failed (error %d)\n", ret); return ret; } @@ -650,8 +674,6 @@ static int sugov_exit(struct cpufreq_policy *policy) struct sugov_tunables *tunables = sg_policy->tunables; unsigned int count; - cpufreq_disable_fast_switch(policy); - mutex_lock(&global_tunables_lock); count = gov_attr_set_put(&tunables->attr_set, &sg_policy->tunables_hook); @@ -664,6 +686,7 @@ static int sugov_exit(struct cpufreq_policy *policy) sugov_kthread_stop(sg_policy); sugov_policy_free(sg_policy); + cpufreq_disable_fast_switch(policy); return 0; } @@ -681,25 +704,19 @@ static int sugov_start(struct cpufreq_policy *policy) sg_policy->next_freq = UINT_MAX; sg_policy->work_in_progress = false; sg_policy->need_freq_update = false; + sg_policy->cached_raw_freq = 0; for_each_cpu(cpu, policy->cpus) { struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu); + memset(sg_cpu, 0, sizeof(*sg_cpu)); sg_cpu->sg_policy = sg_policy; - if (policy_is_shared(policy)) { - sg_cpu->util = 0; - sg_cpu->max = 0; - sg_cpu->flags = SCHED_CPUFREQ_DL; - sg_cpu->last_update = 0; - sg_cpu->cached_raw_freq = 0; - sg_cpu->iowait_boost = 0; - sg_cpu->iowait_boost_max = policy->cpuinfo.max_freq; - cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, - sugov_update_shared); - } else { - cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, - sugov_update_single); - } + sg_cpu->flags = SCHED_CPUFREQ_DL; + sg_cpu->iowait_boost_max = policy->cpuinfo.max_freq; + cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, + policy_is_shared(policy) ? + sugov_update_shared : + sugov_update_single); } return 0; } @@ -714,9 +731,10 @@ static int sugov_stop(struct cpufreq_policy *policy) synchronize_sched(); - irq_work_sync(&sg_policy->irq_work); - kthread_cancel_work_sync(&sg_policy->work); - + if (!policy->fast_switch_enabled) { + irq_work_sync(&sg_policy->irq_work); + kthread_cancel_work_sync(&sg_policy->work); + } return 0; } diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c index 1d00cf8c00fa..14225d5d8617 100644 --- a/kernel/sched/cpupri.c +++ b/kernel/sched/cpupri.c @@ -279,3 +279,14 @@ void cpupri_cleanup(struct cpupri *cp) for (i = 0; i < CPUPRI_NR_PRIORITIES; i++) free_cpumask_var(cp->pri_to_cpu[i].mask); } + +/* + * cpupri_check_rt - check if CPU has a RT task + * should be called from rcu-sched read section. + */ +bool cpupri_check_rt(void) +{ + int cpu = raw_smp_processor_id(); + + return cpu_rq(cpu)->rd->cpupri.cpu_to_pri[cpu] > CPUPRI_NORMAL; +} diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 422438d43d90..853064319b0d 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -35,6 +35,8 @@ #include "sched.h" #include <trace/events/sched.h> #include "tune.h" +#include "walt.h" + /* * Targeted preemption latency for CPU-bound tasks: * (default: 6ms * (1 + ilog(ncpus)), units: nanoseconds) @@ -6548,9 +6550,11 @@ static int find_new_capacity(struct energy_env *eenv, return idx; } -static int group_idle_state(struct sched_group *sg) +static int group_idle_state(struct energy_env *eenv, struct sched_group *sg) { int i, state = INT_MAX; + int src_in_grp, dst_in_grp; + long grp_util = 0; /* Find the shallowest idle state in the sched group. */ for_each_cpu(i, sched_group_cpus(sg)) @@ -6559,6 +6563,54 @@ static int group_idle_state(struct sched_group *sg) /* Take non-cpuidle idling into account (active idle/arch_cpu_idle()) */ state++; + /* + * Try to estimate if a deeper idle state is + * achievable when we move the task. + */ + for_each_cpu(i, sched_group_cpus(sg)) + grp_util += cpu_util(i); + + src_in_grp = cpumask_test_cpu(eenv->src_cpu, sched_group_cpus(sg)); + dst_in_grp = cpumask_test_cpu(eenv->dst_cpu, sched_group_cpus(sg)); + if (src_in_grp == dst_in_grp) { + /* both CPUs under consideration are in the same group or not in + * either group, migration should leave idle state the same. + */ + goto end; + } + /* add or remove util as appropriate to indicate what group util + * will be (worst case - no concurrent execution) after moving the task + */ + grp_util += src_in_grp ? -eenv->util_delta : eenv->util_delta; + + if (grp_util <= + ((long)sg->sgc->max_capacity * (int)sg->group_weight)) { + /* after moving, this group is at most partly + * occupied, so it should have some idle time. + */ + int max_idle_state_idx = sg->sge->nr_idle_states - 2; + int new_state = grp_util * max_idle_state_idx; + if (grp_util <= 0) + /* group will have no util, use lowest state */ + new_state = max_idle_state_idx + 1; + else { + /* for partially idle, linearly map util to idle + * states, excluding the lowest one. This does not + * correspond to the state we expect to enter in + * reality, but an indication of what might happen. + */ + new_state = min(max_idle_state_idx, (int) + (new_state / sg->sgc->max_capacity)); + new_state = max_idle_state_idx - new_state; + } + state = new_state; + } else { + /* After moving, the group will be fully occupied + * so assume it will not be idle at all. + */ + state = 0; + } +end: return state; } @@ -6631,8 +6683,9 @@ static int sched_group_energy(struct energy_env *eenv) } } - idle_idx = group_idle_state(sg); + idle_idx = group_idle_state(eenv, sg); group_util = group_norm_util(eenv, sg); + sg_busy_energy = (group_util * sg->sge->cap_states[cap_idx].power) >> SCHED_CAPACITY_SHIFT; sg_idle_energy = ((SCHED_LOAD_SCALE-group_util) @@ -7337,48 +7390,59 @@ static int start_cpu(bool boosted) return boosted ? rd->max_cap_orig_cpu : rd->min_cap_orig_cpu; } -static inline int find_best_target(struct task_struct *p, bool boosted, bool prefer_idle) +static inline int find_best_target(struct task_struct *p, int *backup_cpu, + bool boosted, bool prefer_idle) { - int target_cpu = -1; - unsigned long target_util = prefer_idle ? ULONG_MAX : 0; - unsigned long backup_capacity = ULONG_MAX; - int best_idle_cpu = -1; - int best_idle_cstate = INT_MAX; - int backup_cpu = -1; + unsigned long best_idle_min_cap_orig = ULONG_MAX; unsigned long min_util = boosted_task_util(p); + unsigned long target_capacity = ULONG_MAX; + unsigned long min_wake_util = ULONG_MAX; + unsigned long target_max_spare_cap = 0; + unsigned long target_util = ULONG_MAX; + unsigned long best_active_util = ULONG_MAX; + int best_idle_cstate = INT_MAX; struct sched_domain *sd; struct sched_group *sg; - int cpu = start_cpu(boosted); + int best_active_cpu = -1; + int best_idle_cpu = -1; + int target_cpu = -1; + int cpu, i; + + *backup_cpu = -1; schedstat_inc(p, se.statistics.nr_wakeups_fbt_attempts); schedstat_inc(this_rq(), eas_stats.fbt_attempts); + /* Find start CPU based on boost value */ + cpu = start_cpu(boosted); if (cpu < 0) { schedstat_inc(p, se.statistics.nr_wakeups_fbt_no_cpu); schedstat_inc(this_rq(), eas_stats.fbt_no_cpu); - return target_cpu; + return -1; } + /* Find SD for the start CPU */ sd = rcu_dereference(per_cpu(sd_ea, cpu)); - if (!sd) { schedstat_inc(p, se.statistics.nr_wakeups_fbt_no_sd); schedstat_inc(this_rq(), eas_stats.fbt_no_sd); - return target_cpu; + return -1; } + /* Scan CPUs in all SDs */ sg = sd->groups; - do { - int i; - for_each_cpu_and(i, tsk_cpus_allowed(p), sched_group_cpus(sg)) { - unsigned long cur_capacity, new_util, wake_util; - unsigned long min_wake_util = ULONG_MAX; + unsigned long capacity_curr = capacity_curr_of(i); + unsigned long capacity_orig = capacity_orig_of(i); + unsigned long wake_util, new_util; if (!cpu_online(i)) continue; + if (walt_cpu_high_irqload(i)) + continue; + /* * p's blocked utilization is still accounted for on prev_cpu * so prev_cpu will receive a negative bias due to the double @@ -7393,65 +7457,204 @@ static inline int find_best_target(struct task_struct *p, bool boosted, bool pre * than the one required to boost the task. */ new_util = max(min_util, new_util); - - if (new_util > capacity_orig_of(i)) + if (new_util > capacity_orig) continue; /* - * Unconditionally favoring tasks that prefer idle cpus to + * Case A) Latency sensitive tasks + * + * Unconditionally favoring tasks that prefer idle CPU to * improve latency. + * + * Looking for: + * - an idle CPU, whatever its idle_state is, since + * the first CPUs we explore are more likely to be + * reserved for latency sensitive tasks. + * - a non idle CPU where the task fits in its current + * capacity and has the maximum spare capacity. + * - a non idle CPU with lower contention from other + * tasks and running at the lowest possible OPP. + * + * The last two goals tries to favor a non idle CPU + * where the task can run as if it is "almost alone". + * A maximum spare capacity CPU is favoured since + * the task already fits into that CPU's capacity + * without waiting for an OPP chance. + * + * The following code path is the only one in the CPUs + * exploration loop which is always used by + * prefer_idle tasks. It exits the loop with wither a + * best_active_cpu or a target_cpu which should + * represent an optimal choice for latency sensitive + * tasks. */ - if (idle_cpu(i) && prefer_idle) { - schedstat_inc(p, se.statistics.nr_wakeups_fbt_pref_idle); - schedstat_inc(this_rq(), eas_stats.fbt_pref_idle); - return i; - } + if (prefer_idle) { - cur_capacity = capacity_curr_of(i); - - if (new_util < cur_capacity) { - if (cpu_rq(i)->nr_running) { - /* - * Find a target cpu with the lowest/highest - * utilization if prefer_idle/!prefer_idle. - */ - if (prefer_idle) { - /* Favor the CPU that last ran the task */ - if (new_util > target_util || - wake_util > min_wake_util) - continue; - min_wake_util = wake_util; - target_util = new_util; - target_cpu = i; - } else if (target_util < new_util) { - target_util = new_util; - target_cpu = i; - } - } else if (!prefer_idle) { - int idle_idx = idle_get_state_idx(cpu_rq(i)); + /* + * Case A.1: IDLE CPU + * Return the first IDLE CPU we find. + */ + if (idle_cpu(i)) { + schedstat_inc(p, se.statistics.nr_wakeups_fbt_pref_idle); + schedstat_inc(this_rq(), eas_stats.fbt_pref_idle); - if (best_idle_cpu < 0 || - (sysctl_sched_cstate_aware && - best_idle_cstate > idle_idx)) { - best_idle_cstate = idle_idx; - best_idle_cpu = i; - } + trace_sched_find_best_target(p, + prefer_idle, min_util, + cpu, best_idle_cpu, + best_active_cpu, i); + + return i; } - } else if (backup_capacity > cur_capacity) { - /* Find a backup cpu with least capacity. */ - backup_capacity = cur_capacity; - backup_cpu = i; + + /* + * Case A.2: Target ACTIVE CPU + * Favor CPUs with max spare capacity. + */ + if ((capacity_curr > new_util) && + (capacity_orig - new_util > target_max_spare_cap)) { + target_max_spare_cap = capacity_orig - new_util; + target_cpu = i; + continue; + } + if (target_cpu != -1) + continue; + + + /* + * Case A.3: Backup ACTIVE CPU + * Favor CPUs with: + * - lower utilization due to other tasks + * - lower utilization with the task in + */ + if (wake_util > min_wake_util) + continue; + if (new_util > best_active_util) + continue; + min_wake_util = wake_util; + best_active_util = new_util; + best_active_cpu = i; + continue; } + + /* + * Case B) Non latency sensitive tasks on IDLE CPUs. + * + * Find an optimal backup IDLE CPU for non latency + * sensitive tasks. + * + * Looking for: + * - minimizing the capacity_orig, + * i.e. preferring LITTLE CPUs + * - favoring shallowest idle states + * i.e. avoid to wakeup deep-idle CPUs + * + * The following code path is used by non latency + * sensitive tasks if IDLE CPUs are available. If at + * least one of such CPUs are available it sets the + * best_idle_cpu to the most suitable idle CPU to be + * selected. + * + * If idle CPUs are available, favour these CPUs to + * improve performances by spreading tasks. + * Indeed, the energy_diff() computed by the caller + * will take care to ensure the minimization of energy + * consumptions without affecting performance. + */ + if (idle_cpu(i)) { + int idle_idx = idle_get_state_idx(cpu_rq(i)); + + /* Select idle CPU with lower cap_orig */ + if (capacity_orig > best_idle_min_cap_orig) + continue; + + /* + * Skip CPUs in deeper idle state, but only + * if they are also less energy efficient. + * IOW, prefer a deep IDLE LITTLE CPU vs a + * shallow idle big CPU. + */ + if (sysctl_sched_cstate_aware && + best_idle_cstate <= idle_idx) + continue; + + /* Keep track of best idle CPU */ + best_idle_min_cap_orig = capacity_orig; + best_idle_cstate = idle_idx; + best_idle_cpu = i; + continue; + } + + /* + * Case C) Non latency sensitive tasks on ACTIVE CPUs. + * + * Pack tasks in the most energy efficient capacities. + * + * This task packing strategy prefers more energy + * efficient CPUs (i.e. pack on smaller maximum + * capacity CPUs) while also trying to spread tasks to + * run them all at the lower OPP. + * + * This assumes for example that it's more energy + * efficient to run two tasks on two CPUs at a lower + * OPP than packing both on a single CPU but running + * that CPU at an higher OPP. + * + * Thus, this case keep track of the CPU with the + * smallest maximum capacity and highest spare maximum + * capacity. + */ + + /* Favor CPUs with smaller capacity */ + if (capacity_orig > target_capacity) + continue; + + /* Favor CPUs with maximum spare capacity */ + if ((capacity_orig - new_util) < target_max_spare_cap) + continue; + + target_max_spare_cap = capacity_orig - new_util; + target_capacity = capacity_orig; + target_util = new_util; + target_cpu = i; } + } while (sg = sg->next, sg != sd->groups); - if (target_cpu < 0) - target_cpu = best_idle_cpu >= 0 ? best_idle_cpu : backup_cpu; + /* + * For non latency sensitive tasks, cases B and C in the previous loop, + * we pick the best IDLE CPU only if we was not able to find a target + * ACTIVE CPU. + * + * Policies priorities: + * + * - prefer_idle tasks: + * + * a) IDLE CPU available, we return immediately + * b) ACTIVE CPU where task fits and has the bigger maximum spare + * capacity (i.e. target_cpu) + * c) ACTIVE CPU with less contention due to other tasks + * (i.e. best_active_cpu) + * + * - NON prefer_idle tasks: + * + * a) ACTIVE CPU: target_cpu + * b) IDLE CPU: best_idle_cpu + */ + if (target_cpu == -1) + target_cpu = prefer_idle + ? best_active_cpu + : best_idle_cpu; + else + *backup_cpu = prefer_idle + ? best_active_cpu + : best_idle_cpu; - if (target_cpu >= 0) { - schedstat_inc(p, se.statistics.nr_wakeups_fbt_count); - schedstat_inc(this_rq(), eas_stats.fbt_count); - } + trace_sched_find_best_target(p, prefer_idle, min_util, cpu, + best_idle_cpu, best_active_cpu, + target_cpu); + + schedstat_inc(p, se.statistics.nr_wakeups_fbt_count); + schedstat_inc(this_rq(), eas_stats.fbt_count); return target_cpu; } @@ -7483,7 +7686,7 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync) { struct sched_domain *sd; - int target_cpu = prev_cpu, tmp_target; + int target_cpu = prev_cpu, tmp_target, tmp_backup; bool boosted, prefer_idle; schedstat_inc(p, se.statistics.nr_wakeups_secb_attempts); @@ -7508,9 +7711,11 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync prefer_idle = 0; #endif + sync_entity_load_avg(&p->se); + sd = rcu_dereference(per_cpu(sd_ea, prev_cpu)); /* Find a cpu with sufficient capacity */ - tmp_target = find_best_target(p, boosted, prefer_idle); + tmp_target = find_best_target(p, &tmp_backup, boosted, prefer_idle); if (!sd) goto unlock; @@ -7539,10 +7744,15 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync } if (energy_diff(&eenv) >= 0) { - schedstat_inc(p, se.statistics.nr_wakeups_secb_no_nrg_sav); - schedstat_inc(this_rq(), eas_stats.secb_no_nrg_sav); - target_cpu = prev_cpu; - goto unlock; + /* No energy saving for target_cpu, try backup */ + target_cpu = tmp_backup; + eenv.dst_cpu = target_cpu; + if (tmp_backup < 0 || energy_diff(&eenv) >= 0) { + schedstat_inc(p, se.statistics.nr_wakeups_secb_no_nrg_sav); + schedstat_inc(this_rq(), eas_stats.secb_no_nrg_sav); + target_cpu = prev_cpu; + goto unlock; + } } schedstat_inc(p, se.statistics.nr_wakeups_secb_nrg_sav); @@ -7584,16 +7794,9 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f return select_best_cpu(p, prev_cpu, 0, sync); #endif - if (sd_flag & SD_BALANCE_WAKE) { - /* - * do wake_cap unconditionally as it causes task and cpu - * utilization to be synced, and we need that for energy - * aware wakeups - */ - int _wake_cap = wake_cap(p, cpu, prev_cpu); - want_affine = !wake_wide(p) && !_wake_cap + if (sd_flag & SD_BALANCE_WAKE) + want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu) && cpumask_test_cpu(cpu, tsk_cpus_allowed(p)); - } if (energy_aware() && !(cpu_rq(prev_cpu)->rd->overutilized)) return select_energy_cpu_brute(p, prev_cpu, sync); @@ -9189,6 +9392,38 @@ group_type group_classify(struct sched_group *group, return group_other; } +#ifdef CONFIG_NO_HZ_COMMON +/* + * idle load balancing data + * - used by the nohz balance, but we want it available here + * so that we can see which CPUs have no tick. + */ +static struct { + cpumask_var_t idle_cpus_mask; + atomic_t nr_cpus; + unsigned long next_balance; /* in jiffy units */ +} nohz ____cacheline_aligned; + +static inline void update_cpu_stats_if_tickless(struct rq *rq) +{ + /* only called from update_sg_lb_stats when irqs are disabled */ + if (cpumask_test_cpu(rq->cpu, nohz.idle_cpus_mask)) { + /* rate limit updates to once-per-jiffie at most */ + if (READ_ONCE(jiffies) <= rq->last_load_update_tick) + return; + + raw_spin_lock(&rq->lock); + update_rq_clock(rq); + update_idle_cpu_load(rq); + update_cfs_rq_load_avg(rq->clock_task, &rq->cfs, false); + raw_spin_unlock(&rq->lock); + } +} + +#else +static inline void update_cpu_stats_if_tickless(struct rq *rq) { } +#endif + /** * update_sg_lb_stats - Update sched_group's statistics for load balancing. * @env: The load balancing environment. @@ -9220,6 +9455,12 @@ static inline void update_sg_lb_stats(struct lb_env *env, if (cpu_isolated(i)) continue; + /* if we are entering idle and there are CPUs with + * their tick stopped, do an update for them + */ + if (env->idle == CPU_NEWLY_IDLE) + update_cpu_stats_if_tickless(rq); + /* Bias balancing toward cpus of our domain */ if (local_group) load = target_load(i, load_idx); @@ -10680,11 +10921,6 @@ static inline int on_null_domain(struct rq *rq) * needed, they will kick the idle load balancer, which then does idle * load balancing for all the idle CPUs. */ -static struct { - cpumask_var_t idle_cpus_mask; - atomic_t nr_cpus; - unsigned long next_balance; /* in jiffy units */ -} nohz ____cacheline_aligned; #ifdef CONFIG_SCHED_HMP static inline int find_new_hmp_ilb(int type) @@ -11111,6 +11347,10 @@ static inline int _nohz_kick_needed(struct rq *rq, int cpu, int *type) (!energy_aware() || cpu_overutilized(cpu))) return true; + /* Do idle load balance if there have misfit task */ + if (energy_aware() && rq->misfit_task) + return 1; + return (rq->nr_running >= 2); } diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index c03d51a017bf..ee095f4e7230 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1823,6 +1823,7 @@ static int find_lowest_rq_hmp(struct task_struct *task) * the best one based on our affinity and topology. */ +retry: for_each_sched_cluster(cluster) { if (boost_on_big && cluster->capacity != max_possible_capacity) continue; @@ -1830,6 +1831,15 @@ static int find_lowest_rq_hmp(struct task_struct *task) cpumask_and(&candidate_mask, &cluster->cpus, lowest_mask); cpumask_andnot(&candidate_mask, &candidate_mask, cpu_isolated_mask); + /* + * When placement boost is active, if there is no eligible CPU + * in the highest capacity cluster, we fallback to the other + * clusters. So clear the CPUs of the traversed cluster from + * the lowest_mask. + */ + if (unlikely(boost_on_big)) + cpumask_andnot(lowest_mask, lowest_mask, + &cluster->cpus); if (cpumask_empty(&candidate_mask)) continue; @@ -1869,6 +1879,11 @@ static int find_lowest_rq_hmp(struct task_struct *task) break; } + if (unlikely(boost_on_big && best_cpu == -1)) { + boost_on_big = 0; + goto retry; + } + return best_cpu; } #endif /* CONFIG_SCHED_HMP */ diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c index 6e053bd9830c..92c3aae8e056 100644 --- a/kernel/sched/walt.c +++ b/kernel/sched/walt.c @@ -72,7 +72,15 @@ static cpumask_t mpc_mask = CPU_MASK_ALL; __read_mostly unsigned int walt_ravg_window = 20000000; /* Min window size (in ns) = 10ms */ +#ifdef CONFIG_HZ_300 +/* + * Tick interval becomes to 3333333 due to + * rounding error when HZ=300. + */ +#define MIN_SCHED_RAVG_WINDOW (3333333 * 6) +#else #define MIN_SCHED_RAVG_WINDOW 10000000 +#endif /* Max window size (in ns) = 1s */ #define MAX_SCHED_RAVG_WINDOW 1000000000 diff --git a/kernel/sched/walt.h b/kernel/sched/walt.h index e181c87a928d..f56c4da16d0b 100644 --- a/kernel/sched/walt.h +++ b/kernel/sched/walt.h @@ -55,6 +55,8 @@ static inline void walt_migrate_sync_cpu(int cpu) { } static inline void walt_init_cpu_efficiency(void) { } static inline u64 walt_ktime_clock(void) { return 0; } +#define walt_cpu_high_irqload(cpu) false + #endif /* CONFIG_SCHED_WALT */ extern unsigned int walt_disabled; diff --git a/kernel/softirq.c b/kernel/softirq.c index 39ffd41594ce..9029227e5f57 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -234,6 +234,8 @@ static inline bool lockdep_softirq_start(void) { return false; } static inline void lockdep_softirq_end(bool in_hardirq) { } #endif +#define long_softirq_pending() (local_softirq_pending() & LONG_SOFTIRQ_MASK) +#define defer_for_rt() (long_softirq_pending() && cpupri_check_rt()) asmlinkage __visible void __do_softirq(void) { unsigned long end = jiffies + MAX_SOFTIRQ_TIME; @@ -297,6 +299,7 @@ restart: pending = local_softirq_pending(); if (pending) { if (time_before(jiffies, end) && !need_resched() && + !defer_for_rt() && --max_restart) goto restart; @@ -349,7 +352,7 @@ void irq_enter(void) static inline void invoke_softirq(void) { - if (!force_irqthreads) { + if (!force_irqthreads && !defer_for_rt()) { #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK /* * We can safely execute softirq on the current stack if diff --git a/kernel/sysctl.c b/kernel/sysctl.c index f27d2ba78d14..8576e6385d63 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2380,9 +2380,12 @@ static int do_proc_douintvec_conv(bool *negp, unsigned long *lvalp, if (write) { if (*negp) return -EINVAL; + if (*lvalp > UINT_MAX) + return -EINVAL; *valp = *lvalp; } else { unsigned int val = *valp; + *negp = false; *lvalp = (unsigned long)val; } return 0; diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 1a4de0022cc5..ceec77c652b5 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -848,7 +848,8 @@ static int alarm_timer_set(struct k_itimer *timr, int flags, * Rate limit to the tick as a hot fix to prevent DOS. Will be * mopped up later. */ - if (ktime_to_ns(timr->it.alarm.interval) < TICK_NSEC) + if (timr->it.alarm.interval.tv64 && + ktime_to_ns(timr->it.alarm.interval) < TICK_NSEC) timr->it.alarm.interval = ktime_set(0, TICK_NSEC); exp = timespec_to_ktime(new_setting->it_value); diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index ec2102104cb8..333f627a3a3b 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -896,6 +896,18 @@ ktime_t tick_nohz_get_sleep_length(void) return ts->sleep_length; } +/** + * tick_nohz_get_idle_calls - return the current idle calls counter value + * + * Called from the schedutil frequency scaling governor in scheduler context. + */ +unsigned long tick_nohz_get_idle_calls(void) +{ + struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched); + + return ts->idle_calls; +} + static void tick_nohz_account_idle_ticks(struct tick_sched *ts) { #ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 34b2a0d5cf1a..eba904bae48c 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3535,7 +3535,7 @@ match_records(struct ftrace_hash *hash, char *func, int len, char *mod) int exclude_mod = 0; int found = 0; int ret; - int clear_filter; + int clear_filter = 0; if (func) { func_g.type = filter_parse_regex(func, len, &func_g.search, diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 60d246c4eefa..a579a874045b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1362,7 +1362,7 @@ static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED; struct saved_cmdlines_buffer { unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1]; unsigned *map_cmdline_to_pid; - unsigned *saved_tgids; + unsigned *map_cmdline_to_tgid; unsigned cmdline_num; int cmdline_idx; char *saved_cmdlines; @@ -1396,9 +1396,10 @@ static int allocate_cmdlines_buffer(unsigned int val, return -ENOMEM; } - s->saved_tgids = kmalloc_array(val, sizeof(*s->saved_tgids), - GFP_KERNEL); - if (!s->saved_tgids) { + s->map_cmdline_to_tgid = kmalloc_array(val, + sizeof(*s->map_cmdline_to_tgid), + GFP_KERNEL); + if (!s->map_cmdline_to_tgid) { kfree(s->map_cmdline_to_pid); kfree(s->saved_cmdlines); return -ENOMEM; @@ -1410,8 +1411,8 @@ static int allocate_cmdlines_buffer(unsigned int val, sizeof(s->map_pid_to_cmdline)); memset(s->map_cmdline_to_pid, NO_CMDLINE_MAP, val * sizeof(*s->map_cmdline_to_pid)); - memset(s->saved_tgids, 0, - val * sizeof(*s->saved_tgids)); + memset(s->map_cmdline_to_tgid, NO_CMDLINE_MAP, + val * sizeof(*s->map_cmdline_to_tgid)); return 0; } @@ -1577,14 +1578,17 @@ static int trace_save_cmdline(struct task_struct *tsk) if (!tsk->pid || unlikely(tsk->pid > PID_MAX_DEFAULT)) return 0; + preempt_disable(); /* * It's not the end of the world if we don't get * the lock, but we also don't want to spin * nor do we want to disable interrupts, * so if we miss here, then better luck next time. */ - if (!arch_spin_trylock(&trace_cmdline_lock)) + if (!arch_spin_trylock(&trace_cmdline_lock)) { + preempt_enable(); return 0; + } idx = savedcmd->map_pid_to_cmdline[tsk->pid]; if (idx == NO_CMDLINE_MAP) { @@ -1607,8 +1611,9 @@ static int trace_save_cmdline(struct task_struct *tsk) } set_cmdline(idx, tsk->comm); - savedcmd->saved_tgids[idx] = tsk->tgid; + savedcmd->map_cmdline_to_tgid[idx] = tsk->tgid; arch_spin_unlock(&trace_cmdline_lock); + preempt_enable(); return 1; } @@ -1650,19 +1655,29 @@ void trace_find_cmdline(int pid, char comm[]) preempt_enable(); } -int trace_find_tgid(int pid) +static int __find_tgid_locked(int pid) { unsigned map; int tgid; - preempt_disable(); - arch_spin_lock(&trace_cmdline_lock); map = savedcmd->map_pid_to_cmdline[pid]; if (map != NO_CMDLINE_MAP) - tgid = savedcmd->saved_tgids[map]; + tgid = savedcmd->map_cmdline_to_tgid[map]; else tgid = -1; + return tgid; +} + +int trace_find_tgid(int pid) +{ + int tgid; + + preempt_disable(); + arch_spin_lock(&trace_cmdline_lock); + + tgid = __find_tgid_locked(pid); + arch_spin_unlock(&trace_cmdline_lock); preempt_enable(); @@ -3979,10 +3994,15 @@ tracing_saved_cmdlines_size_read(struct file *filp, char __user *ubuf, { char buf[64]; int r; + unsigned int n; + preempt_disable(); arch_spin_lock(&trace_cmdline_lock); - r = scnprintf(buf, sizeof(buf), "%u\n", savedcmd->cmdline_num); + n = savedcmd->cmdline_num; arch_spin_unlock(&trace_cmdline_lock); + preempt_enable(); + + r = scnprintf(buf, sizeof(buf), "%u\n", n); return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); } @@ -3991,7 +4011,7 @@ static void free_saved_cmdlines_buffer(struct saved_cmdlines_buffer *s) { kfree(s->saved_cmdlines); kfree(s->map_cmdline_to_pid); - kfree(s->saved_tgids); + kfree(s->map_cmdline_to_tgid); kfree(s); } @@ -4008,10 +4028,12 @@ static int tracing_resize_saved_cmdlines(unsigned int val) return -ENOMEM; } + preempt_disable(); arch_spin_lock(&trace_cmdline_lock); savedcmd_temp = savedcmd; savedcmd = s; arch_spin_unlock(&trace_cmdline_lock); + preempt_enable(); free_saved_cmdlines_buffer(savedcmd_temp); return 0; @@ -4230,33 +4252,61 @@ tracing_saved_tgids_read(struct file *file, char __user *ubuf, char *file_buf; char *buf; int len = 0; - int pid; int i; + int *pids; + int n = 0; - file_buf = kmalloc(savedcmd->cmdline_num*(16+1+16), GFP_KERNEL); - if (!file_buf) - return -ENOMEM; + preempt_disable(); + arch_spin_lock(&trace_cmdline_lock); - buf = file_buf; + pids = kmalloc_array(savedcmd->cmdline_num, 2*sizeof(int), GFP_KERNEL); + if (!pids) { + arch_spin_unlock(&trace_cmdline_lock); + preempt_enable(); + return -ENOMEM; + } for (i = 0; i < savedcmd->cmdline_num; i++) { - int tgid; - int r; + int pid; pid = savedcmd->map_cmdline_to_pid[i]; if (pid == -1 || pid == NO_CMDLINE_MAP) continue; - tgid = trace_find_tgid(pid); - r = sprintf(buf, "%d %d\n", pid, tgid); + pids[n] = pid; + pids[n+1] = __find_tgid_locked(pid); + n += 2; + } + arch_spin_unlock(&trace_cmdline_lock); + preempt_enable(); + + if (n == 0) { + kfree(pids); + return 0; + } + + /* enough to hold max pair of pids + space, lr and nul */ + len = n * 12; + file_buf = kmalloc(len, GFP_KERNEL); + if (!file_buf) { + kfree(pids); + return -ENOMEM; + } + + buf = file_buf; + for (i = 0; i < n && len > 0; i += 2) { + int r; + + r = snprintf(buf, len, "%d %d\n", pids[i], pids[i+1]); buf += r; - len += r; + len -= r; } len = simple_read_from_buffer(ubuf, cnt, ppos, - file_buf, len); + file_buf, buf - file_buf); kfree(file_buf); + kfree(pids); return len; } @@ -6847,6 +6897,7 @@ static int instance_rmdir(const char *name) } kfree(tr->topts); + free_cpumask_var(tr->tracing_cpumask); kfree(tr->name); kfree(tr); diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 12ea4ea619ee..e9092a0247bf 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -659,30 +659,25 @@ static int create_trace_kprobe(int argc, char **argv) pr_info("Probe point is not specified.\n"); return -EINVAL; } - if (isdigit(argv[1][0])) { - if (is_return) { - pr_info("Return probe point must be a symbol.\n"); - return -EINVAL; - } - /* an address specified */ - ret = kstrtoul(&argv[1][0], 0, (unsigned long *)&addr); - if (ret) { - pr_info("Failed to parse address.\n"); - return ret; - } - } else { + + /* try to parse an address. if that fails, try to read the + * input as a symbol. */ + if (kstrtoul(argv[1], 0, (unsigned long *)&addr)) { /* a symbol specified */ symbol = argv[1]; /* TODO: support .init module functions */ ret = traceprobe_split_symbol_offset(symbol, &offset); if (ret) { - pr_info("Failed to parse symbol.\n"); + pr_info("Failed to parse either an address or a symbol.\n"); return ret; } if (offset && is_return) { pr_info("Return probe must be used without offset.\n"); return -EINVAL; } + } else if (is_return) { + pr_info("Return probe point must be a symbol.\n"); + return -EINVAL; } argc -= 2; argv += 2; diff --git a/mm/mmap.c b/mm/mmap.c index 092729c2cb72..16743bf76a88 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2206,7 +2206,7 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) /* Guard against exceeding limits of the address space. */ address &= PAGE_MASK; - if (address >= TASK_SIZE) + if (address >= (TASK_SIZE & PAGE_MASK)) return -ENOMEM; address += PAGE_SIZE; diff --git a/mm/vmscan.c b/mm/vmscan.c index 94fecacf0ddc..5f6e29f25af9 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2676,7 +2676,7 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc) if (!populated_zone(zone)) continue; - classzone_idx = requested_highidx; + classzone_idx = gfp_zone(sc->gfp_mask); while (!populated_zone(zone->zone_pgdat->node_zones + classzone_idx)) classzone_idx--; diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index e20ae2d3c498..5e4199d5a388 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -292,6 +292,10 @@ static void vlan_sync_address(struct net_device *dev, if (ether_addr_equal(vlan->real_dev_addr, dev->dev_addr)) return; + /* vlan continues to inherit address of lower device */ + if (vlan_dev_inherit_address(vlandev, dev)) + goto out; + /* vlan address was different from the old address and is equal to * the new address */ if (!ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) && @@ -304,6 +308,7 @@ static void vlan_sync_address(struct net_device *dev, !ether_addr_equal(vlandev->dev_addr, dev->dev_addr)) dev_uc_add(dev, vlandev->dev_addr); +out: ether_addr_copy(vlan->real_dev_addr, dev->dev_addr); } diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 9d010a09ab98..cc1557978066 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -109,6 +109,8 @@ int vlan_check_real_dev(struct net_device *real_dev, void vlan_setup(struct net_device *dev); int register_vlan_dev(struct net_device *dev); void unregister_vlan_dev(struct net_device *dev, struct list_head *head); +bool vlan_dev_inherit_address(struct net_device *dev, + struct net_device *real_dev); static inline u32 vlan_get_ingress_priority(struct net_device *dev, u16 vlan_tci) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index fded86508117..ca4dc9031073 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -244,6 +244,17 @@ void vlan_dev_get_realdev_name(const struct net_device *dev, char *result) strncpy(result, vlan_dev_priv(dev)->real_dev->name, 23); } +bool vlan_dev_inherit_address(struct net_device *dev, + struct net_device *real_dev) +{ + if (dev->addr_assign_type != NET_ADDR_STOLEN) + return false; + + ether_addr_copy(dev->dev_addr, real_dev->dev_addr); + call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + return true; +} + static int vlan_dev_open(struct net_device *dev) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); @@ -254,7 +265,8 @@ static int vlan_dev_open(struct net_device *dev) !(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) return -ENETDOWN; - if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) { + if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr) && + !vlan_dev_inherit_address(dev, real_dev)) { err = dev_uc_add(real_dev, dev->dev_addr); if (err < 0) goto out; @@ -558,8 +570,10 @@ static int vlan_dev_init(struct net_device *dev) /* ipv6 shared card related stuff */ dev->dev_id = real_dev->dev_id; - if (is_zero_ether_addr(dev->dev_addr)) - eth_hw_addr_inherit(dev, real_dev); + if (is_zero_ether_addr(dev->dev_addr)) { + ether_addr_copy(dev->dev_addr, real_dev->dev_addr); + dev->addr_assign_type = NET_ADDR_STOLEN; + } if (is_zero_ether_addr(dev->broadcast)) memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 6f2c704e41ab..b166b3e441a5 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -23,6 +23,7 @@ #include <linux/debugfs.h> #include <linux/crypto.h> #include <linux/scatterlist.h> +#include <crypto/algapi.h> #include <crypto/b128ops.h> #include <net/bluetooth/bluetooth.h> @@ -524,7 +525,7 @@ bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16], if (err) return false; - return !memcmp(bdaddr->b, hash, 3); + return !crypto_memneq(bdaddr->b, hash, 3); } int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa) @@ -577,7 +578,7 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]) /* This is unlikely, but we need to check that * we didn't accidentially generate a debug key. */ - if (memcmp(smp->local_sk, debug_sk, 32)) + if (crypto_memneq(smp->local_sk, debug_sk, 32)) break; } smp->debug_key = false; @@ -991,7 +992,7 @@ static u8 smp_random(struct smp_chan *smp) if (ret) return SMP_UNSPECIFIED; - if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { + if (crypto_memneq(smp->pcnf, confirm, sizeof(smp->pcnf))) { BT_ERR("Pairing failed (confirmation values mismatch)"); return SMP_CONFIRM_FAILED; } @@ -1491,7 +1492,7 @@ static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op) smp->rrnd, r, cfm)) return SMP_UNSPECIFIED; - if (memcmp(smp->pcnf, cfm, 16)) + if (crypto_memneq(smp->pcnf, cfm, 16)) return SMP_CONFIRM_FAILED; smp->passkey_round++; @@ -1875,7 +1876,7 @@ static u8 sc_send_public_key(struct smp_chan *smp) /* This is unlikely, but we need to check that * we didn't accidentially generate a debug key. */ - if (memcmp(smp->local_sk, debug_sk, 32)) + if (crypto_memneq(smp->local_sk, debug_sk, 32)) break; } } @@ -2140,7 +2141,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) if (err) return SMP_UNSPECIFIED; - if (memcmp(smp->pcnf, cfm, 16)) + if (crypto_memneq(smp->pcnf, cfm, 16)) return SMP_CONFIRM_FAILED; } else { smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), @@ -2621,7 +2622,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) if (err) return SMP_UNSPECIFIED; - if (memcmp(cfm.confirm_val, smp->pcnf, 16)) + if (crypto_memneq(cfm.confirm_val, smp->pcnf, 16)) return SMP_CONFIRM_FAILED; } @@ -2654,7 +2655,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) else hcon->pending_sec_level = BT_SECURITY_FIPS; - if (!memcmp(debug_pk, smp->remote_pk, 64)) + if (!crypto_memneq(debug_pk, smp->remote_pk, 64)) set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); if (smp->method == DSP_PASSKEY) { @@ -2753,7 +2754,7 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) if (err) return SMP_UNSPECIFIED; - if (memcmp(check->e, e, 16)) + if (crypto_memneq(check->e, e, 16)) return SMP_DHKEY_CHECK_FAILED; if (!hcon->out) { @@ -3463,7 +3464,7 @@ static int __init test_ah(struct crypto_blkcipher *tfm_aes) if (err) return err; - if (memcmp(res, exp, 3)) + if (crypto_memneq(res, exp, 3)) return -EINVAL; return 0; @@ -3493,7 +3494,7 @@ static int __init test_c1(struct crypto_blkcipher *tfm_aes) if (err) return err; - if (memcmp(res, exp, 16)) + if (crypto_memneq(res, exp, 16)) return -EINVAL; return 0; @@ -3518,7 +3519,7 @@ static int __init test_s1(struct crypto_blkcipher *tfm_aes) if (err) return err; - if (memcmp(res, exp, 16)) + if (crypto_memneq(res, exp, 16)) return -EINVAL; return 0; @@ -3550,7 +3551,7 @@ static int __init test_f4(struct crypto_hash *tfm_cmac) if (err) return err; - if (memcmp(res, exp, 16)) + if (crypto_memneq(res, exp, 16)) return -EINVAL; return 0; @@ -3584,10 +3585,10 @@ static int __init test_f5(struct crypto_hash *tfm_cmac) if (err) return err; - if (memcmp(mackey, exp_mackey, 16)) + if (crypto_memneq(mackey, exp_mackey, 16)) return -EINVAL; - if (memcmp(ltk, exp_ltk, 16)) + if (crypto_memneq(ltk, exp_ltk, 16)) return -EINVAL; return 0; @@ -3620,7 +3621,7 @@ static int __init test_f6(struct crypto_hash *tfm_cmac) if (err) return err; - if (memcmp(res, exp, 16)) + if (crypto_memneq(res, exp, 16)) return -EINVAL; return 0; @@ -3674,7 +3675,7 @@ static int __init test_h6(struct crypto_hash *tfm_cmac) if (err) return err; - if (memcmp(res, exp, 16)) + if (crypto_memneq(res, exp, 16)) return -EINVAL; return 0; diff --git a/net/core/dev.c b/net/core/dev.c index 9a3aaba15c5a..1ce844e41eaf 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2552,9 +2552,10 @@ EXPORT_SYMBOL(skb_mac_gso_segment); static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path) { if (tx_path) - return skb->ip_summed != CHECKSUM_PARTIAL; - else - return skb->ip_summed == CHECKSUM_NONE; + return skb->ip_summed != CHECKSUM_PARTIAL && + skb->ip_summed != CHECKSUM_NONE; + + return skb->ip_summed == CHECKSUM_NONE; } /** @@ -2573,11 +2574,12 @@ static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path) struct sk_buff *__skb_gso_segment(struct sk_buff *skb, netdev_features_t features, bool tx_path) { + struct sk_buff *segs; + if (unlikely(skb_needs_check(skb, tx_path))) { int err; - skb_warn_bad_offload(skb); - + /* We're going to init ->check field in TCP or UDP header */ err = skb_cow_head(skb, 0); if (err < 0) return ERR_PTR(err); @@ -2592,7 +2594,12 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb, skb_reset_mac_header(skb); skb_reset_mac_len(skb); - return skb_mac_gso_segment(skb, features); + segs = skb_mac_gso_segment(skb, features); + + if (unlikely(skb_needs_check(skb, tx_path))) + skb_warn_bad_offload(skb); + + return segs; } EXPORT_SYMBOL(__skb_gso_segment); @@ -4383,6 +4390,12 @@ struct packet_offload *gro_find_complete_by_type(__be16 type) } EXPORT_SYMBOL(gro_find_complete_by_type); +static void napi_skb_free_stolen_head(struct sk_buff *skb) +{ + skb_dst_drop(skb); + kmem_cache_free(skbuff_head_cache, skb); +} + static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb) { switch (ret) { @@ -4396,12 +4409,10 @@ static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb) break; case GRO_MERGED_FREE: - if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD) { - skb_dst_drop(skb); - kmem_cache_free(skbuff_head_cache, skb); - } else { + if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD) + napi_skb_free_stolen_head(skb); + else __kfree_skb(skb); - } break; case GRO_HELD: @@ -4467,10 +4478,16 @@ static gro_result_t napi_frags_finish(struct napi_struct *napi, break; case GRO_DROP: - case GRO_MERGED_FREE: napi_reuse_skb(napi, skb); break; + case GRO_MERGED_FREE: + if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD) + napi_skb_free_stolen_head(skb); + else + napi_reuse_skb(napi, skb); + break; + case GRO_MERGED: break; } @@ -7088,8 +7105,8 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, } else { netdev_stats_to_stats64(storage, &dev->stats); } - storage->rx_dropped += atomic_long_read(&dev->rx_dropped); - storage->tx_dropped += atomic_long_read(&dev->tx_dropped); + storage->rx_dropped += (unsigned long)atomic_long_read(&dev->rx_dropped); + storage->tx_dropped += (unsigned long)atomic_long_read(&dev->tx_dropped); return storage; } EXPORT_SYMBOL(dev_get_stats); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 57e5938fd669..9bdd7847ef3a 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2272,6 +2272,8 @@ int tcp_disconnect(struct sock *sk, int flags) tcp_init_send_head(sk); memset(&tp->rx_opt, 0, sizeof(tp->rx_opt)); __sk_dst_reset(sk); + dst_release(sk->sk_rx_dst); + sk->sk_rx_dst = NULL; tcp_saved_syn_free(tp); WARN_ON(inet->inet_num && !icsk->icsk_bind_hash); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 710101376a76..69d52fee247e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2166,8 +2166,7 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head) { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - int cnt, oldcnt; - int err; + int cnt, oldcnt, lost; unsigned int mss; /* Use SACK to deduce losses of new sequences sent during recovery */ const u32 loss_high = tcp_is_sack(tp) ? tp->snd_nxt : tp->high_seq; @@ -2207,9 +2206,10 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head) break; mss = tcp_skb_mss(skb); - err = tcp_fragment(sk, skb, (packets - oldcnt) * mss, - mss, GFP_ATOMIC); - if (err < 0) + /* If needed, chop off the prefix to mark as lost. */ + lost = (packets - oldcnt) * mss; + if (lost < skb->len && + tcp_fragment(sk, skb, lost, mss, GFP_ATOMIC) < 0) break; cnt = packets; } diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 55e928819846..4b707ad4ffbd 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1801,17 +1801,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed) { - if (ifp->flags&IFA_F_PERMANENT) { - spin_lock_bh(&ifp->lock); - addrconf_del_dad_work(ifp); - ifp->flags |= IFA_F_TENTATIVE; - if (dad_failed) - ifp->flags |= IFA_F_DADFAILED; - spin_unlock_bh(&ifp->lock); - if (dad_failed) - ipv6_ifa_notify(0, ifp); - in6_ifa_put(ifp); - } else if (ifp->flags&IFA_F_TEMPORARY) { + if (ifp->flags&IFA_F_TEMPORARY) { struct inet6_ifaddr *ifpub; spin_lock_bh(&ifp->lock); ifpub = ifp->ifpub; @@ -1824,6 +1814,16 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed) spin_unlock_bh(&ifp->lock); } ipv6_del_addr(ifp); + } else if (ifp->flags&IFA_F_PERMANENT || !dad_failed) { + spin_lock_bh(&ifp->lock); + addrconf_del_dad_work(ifp); + ifp->flags |= IFA_F_TENTATIVE; + if (dad_failed) + ifp->flags |= IFA_F_DADFAILED; + spin_unlock_bh(&ifp->lock); + if (dad_failed) + ipv6_ifa_notify(0, ifp); + in6_ifa_put(ifp); } else { ipv6_del_addr(ifp); } @@ -3212,6 +3212,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, { struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct inet6_dev *idev = __in6_dev_get(dev); + struct net *net = dev_net(dev); int run_pending = 0; int err; @@ -3227,7 +3228,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, case NETDEV_CHANGEMTU: /* if MTU under IPV6_MIN_MTU stop IPv6 on this interface. */ if (dev->mtu < IPV6_MIN_MTU) { - addrconf_ifdown(dev, 1); + addrconf_ifdown(dev, dev != net->loopback_dev); break; } @@ -3340,7 +3341,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, * IPV6_MIN_MTU stop IPv6 on this interface. */ if (dev->mtu < IPV6_MIN_MTU) - addrconf_ifdown(dev, 1); + addrconf_ifdown(dev, dev != net->loopback_dev); } break; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 1ac06723f0d7..f60e8caea767 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -767,10 +767,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, goto next_iter; } - if (iter->dst.dev == rt->dst.dev && - iter->rt6i_idev == rt->rt6i_idev && - ipv6_addr_equal(&iter->rt6i_gateway, - &rt->rt6i_gateway)) { + if (rt6_duplicate_nexthop(iter, rt)) { if (rt->rt6i_nsiblings) rt->rt6i_nsiblings = 0; if (!(iter->rt6i_flags & RTF_EXPIRES)) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 72b53b83a720..6177acdf60ac 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1360,7 +1360,7 @@ emsgsize: */ cork->length += length; - if (((length > mtu) || + if ((((length + fragheaderlen) > mtu) || (skb && skb_is_gso(skb))) && (sk->sk_protocol == IPPROTO_UDP) && (rt->dst.dev->features & NETIF_F_UFO) && diff --git a/net/ipv6/route.c b/net/ipv6/route.c index e98613d2f34f..dd37fe0b6a49 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2822,17 +2822,11 @@ static int ip6_route_info_append(struct list_head *rt6_nh_list, struct rt6_info *rt, struct fib6_config *r_cfg) { struct rt6_nh *nh; - struct rt6_info *rtnh; int err = -EEXIST; list_for_each_entry(nh, rt6_nh_list, next) { /* check if rt6_info already exists */ - rtnh = nh->rt6_info; - - if (rtnh->dst.dev == rt->dst.dev && - rtnh->rt6i_idev == rt->rt6i_idev && - ipv6_addr_equal(&rtnh->rt6i_gateway, - &rt->rt6i_gateway)) + if (rt6_duplicate_nexthop(nh->rt6_info, rt)) return err; } diff --git a/net/key/af_key.c b/net/key/af_key.c index e67c28e614b9..2e1050ec2cf0 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -63,8 +63,13 @@ struct pfkey_sock { } u; struct sk_buff *skb; } dump; + struct mutex dump_lock; }; +static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len, + xfrm_address_t *saddr, xfrm_address_t *daddr, + u16 *family); + static inline struct pfkey_sock *pfkey_sk(struct sock *sk) { return (struct pfkey_sock *)sk; @@ -139,6 +144,7 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol, { struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id); struct sock *sk; + struct pfkey_sock *pfk; int err; if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) @@ -153,6 +159,9 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol, if (sk == NULL) goto out; + pfk = pfkey_sk(sk); + mutex_init(&pfk->dump_lock); + sock->ops = &pfkey_ops; sock_init_data(sock, sk); @@ -281,13 +290,23 @@ static int pfkey_do_dump(struct pfkey_sock *pfk) struct sadb_msg *hdr; int rc; + mutex_lock(&pfk->dump_lock); + if (!pfk->dump.dump) { + rc = 0; + goto out; + } + rc = pfk->dump.dump(pfk); - if (rc == -ENOBUFS) - return 0; + if (rc == -ENOBUFS) { + rc = 0; + goto out; + } if (pfk->dump.skb) { - if (!pfkey_can_dump(&pfk->sk)) - return 0; + if (!pfkey_can_dump(&pfk->sk)) { + rc = 0; + goto out; + } hdr = (struct sadb_msg *) pfk->dump.skb->data; hdr->sadb_msg_seq = 0; @@ -298,6 +317,9 @@ static int pfkey_do_dump(struct pfkey_sock *pfk) } pfkey_terminate_dump(pfk); + +out: + mutex_unlock(&pfk->dump_lock); return rc; } @@ -1802,19 +1824,26 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms struct xfrm_address_filter *filter = NULL; struct pfkey_sock *pfk = pfkey_sk(sk); - if (pfk->dump.dump != NULL) + mutex_lock(&pfk->dump_lock); + if (pfk->dump.dump != NULL) { + mutex_unlock(&pfk->dump_lock); return -EBUSY; + } proto = pfkey_satype2proto(hdr->sadb_msg_satype); - if (proto == 0) + if (proto == 0) { + mutex_unlock(&pfk->dump_lock); return -EINVAL; + } if (ext_hdrs[SADB_X_EXT_FILTER - 1]) { struct sadb_x_filter *xfilter = ext_hdrs[SADB_X_EXT_FILTER - 1]; filter = kmalloc(sizeof(*filter), GFP_KERNEL); - if (filter == NULL) + if (filter == NULL) { + mutex_unlock(&pfk->dump_lock); return -ENOMEM; + } memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr, sizeof(xfrm_address_t)); @@ -1830,6 +1859,7 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms pfk->dump.dump = pfkey_dump_sa; pfk->dump.done = pfkey_dump_sa_done; xfrm_state_walk_init(&pfk->dump.u.state, proto, filter); + mutex_unlock(&pfk->dump_lock); return pfkey_do_dump(pfk); } @@ -1922,19 +1952,14 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq) /* addresses present only in tunnel mode */ if (t->mode == XFRM_MODE_TUNNEL) { - u8 *sa = (u8 *) (rq + 1); - int family, socklen; + int err; - family = pfkey_sockaddr_extract((struct sockaddr *)sa, - &t->saddr); - if (!family) - return -EINVAL; - - socklen = pfkey_sockaddr_len(family); - if (pfkey_sockaddr_extract((struct sockaddr *)(sa + socklen), - &t->id.daddr) != family) - return -EINVAL; - t->encap_family = family; + err = parse_sockaddr_pair( + (struct sockaddr *)(rq + 1), + rq->sadb_x_ipsecrequest_len - sizeof(*rq), + &t->saddr, &t->id.daddr, &t->encap_family); + if (err) + return err; } else t->encap_family = xp->family; @@ -1954,7 +1979,11 @@ parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol) if (pol->sadb_x_policy_len * 8 < sizeof(struct sadb_x_policy)) return -EINVAL; - while (len >= sizeof(struct sadb_x_ipsecrequest)) { + while (len >= sizeof(*rq)) { + if (len < rq->sadb_x_ipsecrequest_len || + rq->sadb_x_ipsecrequest_len < sizeof(*rq)) + return -EINVAL; + if ((err = parse_ipsecrequest(xp, rq)) < 0) return err; len -= rq->sadb_x_ipsecrequest_len; @@ -2417,7 +2446,6 @@ out: return err; } -#ifdef CONFIG_NET_KEY_MIGRATE static int pfkey_sockaddr_pair_size(sa_family_t family) { return PFKEY_ALIGN8(pfkey_sockaddr_len(family) * 2); @@ -2429,7 +2457,7 @@ static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len, { int af, socklen; - if (ext_len < pfkey_sockaddr_pair_size(sa->sa_family)) + if (ext_len < 2 || ext_len < pfkey_sockaddr_pair_size(sa->sa_family)) return -EINVAL; af = pfkey_sockaddr_extract(sa, saddr); @@ -2445,6 +2473,7 @@ static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len, return 0; } +#ifdef CONFIG_NET_KEY_MIGRATE static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len, struct xfrm_migrate *m) { @@ -2452,13 +2481,14 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len, struct sadb_x_ipsecrequest *rq2; int mode; - if (len <= sizeof(struct sadb_x_ipsecrequest) || - len < rq1->sadb_x_ipsecrequest_len) + if (len < sizeof(*rq1) || + len < rq1->sadb_x_ipsecrequest_len || + rq1->sadb_x_ipsecrequest_len < sizeof(*rq1)) return -EINVAL; /* old endoints */ err = parse_sockaddr_pair((struct sockaddr *)(rq1 + 1), - rq1->sadb_x_ipsecrequest_len, + rq1->sadb_x_ipsecrequest_len - sizeof(*rq1), &m->old_saddr, &m->old_daddr, &m->old_family); if (err) @@ -2467,13 +2497,14 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len, rq2 = (struct sadb_x_ipsecrequest *)((u8 *)rq1 + rq1->sadb_x_ipsecrequest_len); len -= rq1->sadb_x_ipsecrequest_len; - if (len <= sizeof(struct sadb_x_ipsecrequest) || - len < rq2->sadb_x_ipsecrequest_len) + if (len <= sizeof(*rq2) || + len < rq2->sadb_x_ipsecrequest_len || + rq2->sadb_x_ipsecrequest_len < sizeof(*rq2)) return -EINVAL; /* new endpoints */ err = parse_sockaddr_pair((struct sockaddr *)(rq2 + 1), - rq2->sadb_x_ipsecrequest_len, + rq2->sadb_x_ipsecrequest_len - sizeof(*rq2), &m->new_saddr, &m->new_daddr, &m->new_family); if (err) @@ -2688,14 +2719,18 @@ static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, const struct sadb { struct pfkey_sock *pfk = pfkey_sk(sk); - if (pfk->dump.dump != NULL) + mutex_lock(&pfk->dump_lock); + if (pfk->dump.dump != NULL) { + mutex_unlock(&pfk->dump_lock); return -EBUSY; + } pfk->dump.msg_version = hdr->sadb_msg_version; pfk->dump.msg_portid = hdr->sadb_msg_pid; pfk->dump.dump = pfkey_dump_sp; pfk->dump.done = pfkey_dump_sp_done; xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN); + mutex_unlock(&pfk->dump_lock); return pfkey_do_dump(pfk); } diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 4da560005b0e..dd1649caa2b2 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -845,10 +845,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb, { unsigned int verdict = NF_DROP; - if (IP_VS_FWD_METHOD(cp) != 0) { - pr_err("shouldn't reach here, because the box is on the " - "half connection in the tun/dr module.\n"); - } + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto ignore_cp; /* Ensure the checksum is correct */ if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { @@ -882,6 +880,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb, ip_vs_notrack(skb); else ip_vs_update_conntrack(skb, cp, 0); + +ignore_cp: verdict = NF_ACCEPT; out: @@ -1242,8 +1242,11 @@ ip_vs_out(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, in */ cp = pp->conn_out_get(ipvs, af, skb, &iph); - if (likely(cp)) + if (likely(cp)) { + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto ignore_cp; return handle_response(af, skb, pd, cp, &iph, hooknum); + } if (sysctl_nat_icmp_send(ipvs) && (pp->protocol == IPPROTO_TCP || pp->protocol == IPPROTO_UDP || @@ -1285,9 +1288,15 @@ ip_vs_out(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, in } } } + +out: IP_VS_DBG_PKT(12, af, pp, skb, iph.off, "ip_vs_out: packet continues traversal as normal"); return NF_ACCEPT; + +ignore_cp: + __ip_vs_conn_put(cp); + goto out; } /* diff --git a/net/nfc/core.c b/net/nfc/core.c index 1fe3d3b362c0..c5a2c7e733b3 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -969,6 +969,8 @@ static void nfc_release(struct device *d) kfree(se); } + ida_simple_remove(&nfc_index_ida, dev->idx); + kfree(dev); } @@ -1043,6 +1045,7 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, int tx_headroom, int tx_tailroom) { struct nfc_dev *dev; + int rc; if (!ops->start_poll || !ops->stop_poll || !ops->activate_target || !ops->deactivate_target || !ops->im_transceive) @@ -1055,6 +1058,15 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, if (!dev) return NULL; + rc = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL); + if (rc < 0) + goto err_free_dev; + dev->idx = rc; + + dev->dev.class = &nfc_class; + dev_set_name(&dev->dev, "nfc%d", dev->idx); + device_initialize(&dev->dev); + dev->ops = ops; dev->supported_protocols = supported_protocols; dev->tx_headroom = tx_headroom; @@ -1077,6 +1089,11 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, } return dev; + +err_free_dev: + kfree(dev); + + return ERR_PTR(rc); } EXPORT_SYMBOL(nfc_allocate_device); @@ -1091,14 +1108,6 @@ int nfc_register_device(struct nfc_dev *dev) pr_debug("dev_name=%s\n", dev_name(&dev->dev)); - dev->idx = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL); - if (dev->idx < 0) - return dev->idx; - - dev->dev.class = &nfc_class; - dev_set_name(&dev->dev, "nfc%d", dev->idx); - device_initialize(&dev->dev); - mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; rc = device_add(&dev->dev); @@ -1136,12 +1145,10 @@ EXPORT_SYMBOL(nfc_register_device); */ void nfc_unregister_device(struct nfc_dev *dev) { - int rc, id; + int rc; pr_debug("dev_name=%s\n", dev_name(&dev->dev)); - id = dev->idx; - if (dev->rfkill) { rfkill_unregister(dev->rfkill); rfkill_destroy(dev->rfkill); @@ -1166,8 +1173,6 @@ void nfc_unregister_device(struct nfc_dev *dev) nfc_devlist_generation++; device_del(&dev->dev); mutex_unlock(&nfc_devlist_mutex); - - ida_simple_remove(&nfc_index_ida, id); } EXPORT_SYMBOL(nfc_unregister_device); diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index ecf0a0196f18..9c222a106c7f 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -76,7 +76,8 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) struct sockaddr_nfc_llcp llcp_addr; int len, ret = 0; - if (!addr || addr->sa_family != AF_NFC) + if (!addr || alen < offsetofend(struct sockaddr, sa_family) || + addr->sa_family != AF_NFC) return -EINVAL; pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family); @@ -150,7 +151,8 @@ static int llcp_raw_sock_bind(struct socket *sock, struct sockaddr *addr, struct sockaddr_nfc_llcp llcp_addr; int len, ret = 0; - if (!addr || addr->sa_family != AF_NFC) + if (!addr || alen < offsetofend(struct sockaddr, sa_family) || + addr->sa_family != AF_NFC) return -EINVAL; pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family); @@ -655,8 +657,7 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr, pr_debug("sock %p sk %p flags 0x%x\n", sock, sk, flags); - if (!addr || len < sizeof(struct sockaddr_nfc) || - addr->sa_family != AF_NFC) + if (!addr || len < sizeof(*addr) || addr->sa_family != AF_NFC) return -EINVAL; if (addr->service_name_len == 0 && addr->dsap == 0) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 10c99a578421..67583ad7f610 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -1084,8 +1084,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops, return ndev; free_nfc: - kfree(ndev->nfc_dev); - + nfc_free_device(ndev->nfc_dev); free_nci: kfree(ndev); return NULL; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index f58c1fba1026..12dfb457275d 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -873,7 +873,9 @@ static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info) u32 device_idx, target_idx, protocol; int rc; - if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) + if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || + !info->attrs[NFC_ATTR_TARGET_INDEX] || + !info->attrs[NFC_ATTR_PROTOCOLS]) return -EINVAL; device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index 0936a4a32b47..e353e3255206 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c @@ -78,7 +78,7 @@ int rds_tcp_accept_one(struct socket *sock) struct inet_sock *inet; struct rds_tcp_connection *rs_tcp; - ret = sock_create_kern(sock_net(sock->sk), sock->sk->sk_family, + ret = sock_create_lite(sock->sk->sk_family, sock->sk->sk_type, sock->sk->sk_protocol, &new_sock); if (ret) diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c index ae60f35b363d..b17556c346ce 100644 --- a/net/rmnet_data/rmnet_data_handlers.c +++ b/net/rmnet_data/rmnet_data_handlers.c @@ -476,10 +476,12 @@ static rx_handler_result_t _rmnet_map_ingress_handler(struct sk_buff *skb, if (likely((ckresult == RMNET_MAP_CHECKSUM_OK) || (ckresult == RMNET_MAP_CHECKSUM_SKIPPED))) skb->ip_summed |= CHECKSUM_UNNECESSARY; - else if (ckresult != RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION - && ckresult != RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT - && ckresult != RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET - && ckresult != RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET) { + else if (ckresult != + RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION && + ckresult != RMNET_MAP_CHECKSUM_VALIDATION_FAILED && + ckresult != RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT && + ckresult != RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET && + ckresult != RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET) { rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_INGRESS_BAD_MAP_CKSUM); return RX_HANDLER_CONSUMED; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 5474dc7c125a..ca4ecc246347 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1004,6 +1004,9 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, return sch; } + /* ops->init() failed, we call ->destroy() like qdisc_create_dflt() */ + if (ops->destroy) + ops->destroy(sch); err_out3: dev_put(dev); kfree((char *) sch - sch->padded); diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 13d6f83ec491..45d4b2f22f62 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -636,7 +636,9 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt) q->hhf_arrays[i] = hhf_zalloc(HHF_ARRAYS_LEN * sizeof(u32)); if (!q->hhf_arrays[i]) { - hhf_destroy(sch); + /* Note: hhf_destroy() will be called + * by our caller. + */ return -ENOMEM; } } @@ -647,7 +649,9 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt) q->hhf_valid_bits[i] = hhf_zalloc(HHF_ARRAYS_LEN / BITS_PER_BYTE); if (!q->hhf_valid_bits[i]) { - hhf_destroy(sch); + /* Note: hhf_destroy() will be called + * by our caller. + */ return -ENOMEM; } } diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index 3e82f047caaf..d9c84328e7eb 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -52,7 +52,7 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt) /* pre-allocate qdiscs, attachment can't fail */ priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]), GFP_KERNEL); - if (priv->qdiscs == NULL) + if (!priv->qdiscs) return -ENOMEM; for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { @@ -60,18 +60,14 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt) qdisc = qdisc_create_dflt(dev_queue, default_qdisc_ops, TC_H_MAKE(TC_H_MAJ(sch->handle), TC_H_MIN(ntx + 1))); - if (qdisc == NULL) - goto err; + if (!qdisc) + return -ENOMEM; priv->qdiscs[ntx] = qdisc; qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; } sch->flags |= TCQ_F_MQROOT; return 0; - -err: - mq_destroy(sch); - return -ENOMEM; } static void mq_attach(struct Qdisc *sch) diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index ad70ecf57ce7..66bccc5ff4ea 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -117,20 +117,17 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) /* pre-allocate qdisc, attachment can't fail */ priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]), GFP_KERNEL); - if (priv->qdiscs == NULL) { - err = -ENOMEM; - goto err; - } + if (!priv->qdiscs) + return -ENOMEM; for (i = 0; i < dev->num_tx_queues; i++) { dev_queue = netdev_get_tx_queue(dev, i); qdisc = qdisc_create_dflt(dev_queue, default_qdisc_ops, TC_H_MAKE(TC_H_MAJ(sch->handle), TC_H_MIN(i + 1))); - if (qdisc == NULL) { - err = -ENOMEM; - goto err; - } + if (!qdisc) + return -ENOMEM; + priv->qdiscs[i] = qdisc; qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; } @@ -143,7 +140,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) priv->hw_owned = 1; err = dev->netdev_ops->ndo_setup_tc(dev, qopt->num_tc); if (err) - goto err; + return err; } else { netdev_set_num_tc(dev, qopt->num_tc); for (i = 0; i < qopt->num_tc; i++) @@ -157,10 +154,6 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) sch->flags |= TCQ_F_MQROOT; return 0; - -err: - mqprio_destroy(sch); - return err; } static void mqprio_attach(struct Qdisc *sch) diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 498f0a2cb47f..4431e2833e45 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -742,9 +742,10 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt) q->ht = sfq_alloc(sizeof(q->ht[0]) * q->divisor); q->slots = sfq_alloc(sizeof(q->slots[0]) * q->maxflows); if (!q->ht || !q->slots) { - sfq_destroy(sch); + /* Note: sfq_destroy() will be called by our caller */ return -ENOMEM; } + for (i = 0; i < q->divisor; i++) q->ht[i] = SFQ_EMPTY_SLOT; diff --git a/net/wireless/db.txt b/net/wireless/db.txt index 9ff010cee67e..0727a6e9f780 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -539,6 +539,12 @@ country GH: DFS-FCC (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) +country GI: DFS-ETSI + (2402 - 2482 @ 40), (20) + (5170 - 5250 @ 80), (23), AUTO-BW + (5250 - 5330 @ 80), (23), DFS, AUTO-BW + (5490 - 5710 @ 160), (30), DFS + country GL: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW @@ -679,10 +685,6 @@ country IN: (5170 - 5330 @ 160), (23) (5735 - 5835 @ 80), (30) -country IR: - (2402 - 2482 @ 40), (20) - (5735 - 5835 @ 80), (30) - country IS: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW @@ -731,7 +733,7 @@ country JO: (5170 - 5250 @ 80), (23) (5735 - 5835 @ 80), (23) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - ((57000 - 66000 @ 2160), (40) + (57000 - 66000 @ 2160), (40) country JP: DFS-JP (2402 - 2482 @ 40), (20) @@ -1417,7 +1419,7 @@ country UG: DFS-FCC country US: DFS-FCC (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW + (5170 - 5250 @ 80), (30), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d0d09c290ff8..2a9ec3e05c73 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -302,8 +302,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, [NL80211_ATTR_PID] = { .type = NLA_U32 }, [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, - [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, - .len = WLAN_PMKID_LEN }, + [NL80211_ATTR_PMKID] = { .len = WLAN_PMKID_LEN }, [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, @@ -359,6 +358,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 }, [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, + [NL80211_ATTR_LOCAL_MESH_POWER_MODE] = {. type = NLA_U32 }, [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 }, @@ -6124,6 +6124,10 @@ static int validate_scan_freqs(struct nlattr *freqs) struct nlattr *attr1, *attr2; int n_channels = 0, tmp1, tmp2; + nla_for_each_nested(attr1, freqs, tmp1) + if (nla_len(attr1) != sizeof(u32)) + return 0; + nla_for_each_nested(attr1, freqs, tmp1) { n_channels++; /* diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 0f45e1a3a7d1..c8700399d7fd 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1214,7 +1214,7 @@ static inline int policy_to_flow_dir(int dir) } static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, - const struct flowi *fl) + const struct flowi *fl, u16 family) { struct xfrm_policy *pol; struct net *net = sock_net(sk); @@ -1223,8 +1223,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, read_lock_bh(&net->xfrm.xfrm_policy_lock); pol = rcu_dereference(sk->sk_policy[dir]); if (pol != NULL) { - bool match = xfrm_selector_match(&pol->selector, fl, - sk->sk_family); + bool match = xfrm_selector_match(&pol->selector, fl, family); int err = 0; if (match) { @@ -2171,7 +2170,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, sk = sk_const_to_full_sk(sk); if (sk && sk->sk_policy[XFRM_POLICY_OUT]) { num_pols = 1; - pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl); + pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family); err = xfrm_expand_policies(fl, family, pols, &num_pols, &num_xfrms); if (err < 0) @@ -2450,7 +2449,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, pol = NULL; sk = sk_to_full_sk(sk); if (sk && sk->sk_policy[dir]) { - pol = xfrm_sk_policy_lookup(sk, dir, &fl); + pol = xfrm_sk_policy_lookup(sk, dir, &fl, family); if (IS_ERR(pol)) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); return 0; diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index c72bcb33496a..02996be239bc 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3410,7 +3410,7 @@ sub process { $fixedline =~ s/\s*=\s*$/ = {/; fix_insert_line($fixlinenr, $fixedline); $fixedline = $line; - $fixedline =~ s/^(.\s*){\s*/$1/; + $fixedline =~ s/^(.\s*)\{\s*/$1/; fix_insert_line($fixlinenr, $fixedline); } } @@ -3760,7 +3760,7 @@ sub process { my $fixedline = rtrim($prevrawline) . " {"; fix_insert_line($fixlinenr, $fixedline); $fixedline = $rawline; - $fixedline =~ s/^(.\s*){\s*/$1\t/; + $fixedline =~ s/^(.\s*)\{\s*/$1\t/; if ($fixedline !~ /^\+\s*$/) { fix_insert_line($fixlinenr, $fixedline); } @@ -4249,7 +4249,7 @@ sub process { if (ERROR("SPACING", "space required before the open brace '{'\n" . $herecurr) && $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\))){/$1 {/; + $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\)))\{/$1 {/; } } diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 696ccfa08d10..31898856682e 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -428,7 +428,7 @@ static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key, static struct key *request_master_key(struct encrypted_key_payload *epayload, const u8 **master_key, size_t *master_keylen) { - struct key *mkey = NULL; + struct key *mkey = ERR_PTR(-EINVAL); if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) { diff --git a/security/pfe/pfk_kc.c b/security/pfe/pfk_kc.c index 3a12dd172d04..b428e1b8b11c 100644 --- a/security/pfe/pfk_kc.c +++ b/security/pfe/pfk_kc.c @@ -527,10 +527,12 @@ int pfk_kc_load_key_start(const unsigned char *key, size_t key_size, kc_update_timestamp(entry); entry->state = ACTIVE_ICE_LOADED; - if (async && (!strcmp(s_type, - (char *)PFK_UFS))) + if (!strcmp(s_type, (char *)PFK_UFS)) { + if (async) + entry->loaded_ref_cnt++; + } else { entry->loaded_ref_cnt++; - + } break; } case (FREE): @@ -544,14 +546,16 @@ int pfk_kc_load_key_start(const unsigned char *key, size_t key_size, entry->state = ACTIVE_ICE_LOADED; /* - * only increase ref cnt for async calls, + * In case of UFS only increase ref cnt for async calls, * sync calls from within work thread do not pass * requests further to HW */ - if (async && (!strcmp(s_type, - (char *)PFK_UFS))) + if (!strcmp(s_type, (char *)PFK_UFS)) { + if (async) + entry->loaded_ref_cnt++; + } else { entry->loaded_ref_cnt++; - + } } break; case (ACTIVE_ICE_PRELOAD): @@ -561,9 +565,12 @@ int pfk_kc_load_key_start(const unsigned char *key, size_t key_size, case (ACTIVE_ICE_LOADED): kc_update_timestamp(entry); - if (async && (!strcmp(s_type, - (char *)PFK_UFS))) + if (!strcmp(s_type, (char *)PFK_UFS)) { + if (async) + entry->loaded_ref_cnt++; + } else { entry->loaded_ref_cnt++; + } break; case(SCM_ERROR): ret = entry->scm_error; @@ -621,36 +628,24 @@ void pfk_kc_load_key_end(const unsigned char *key, size_t key_size, return; } - if (!strcmp(s_type, (char *)PFK_UFS)) { - ref_cnt = --entry->loaded_ref_cnt; + ref_cnt = --entry->loaded_ref_cnt; - if (ref_cnt < 0) - pr_err("internal error, ref count should never be negative\n"); + if (ref_cnt < 0) + pr_err("internal error, ref count should never be negative\n"); - if (!ref_cnt) { - entry->state = INACTIVE; - /* - * wake-up invalidation if it's waiting - * for the entry to be released - */ - if (entry->thread_pending) { - tmp_pending = entry->thread_pending; - entry->thread_pending = NULL; - - kc_spin_unlock(); - wake_up_process(tmp_pending); - return; - } - } - } else { + if (!ref_cnt) { entry->state = INACTIVE; /* - * wake-up invalidation if it's waiting - * for the entry to be released - */ + * wake-up invalidation if it's waiting + * for the entry to be released + */ if (entry->thread_pending) { - wake_up_process(entry->thread_pending); + tmp_pending = entry->thread_pending; entry->thread_pending = NULL; + + kc_spin_unlock(); + wake_up_process(tmp_pending); + return; } } diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index c1b87c5800b1..b3fddba4c084 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -936,7 +936,8 @@ static void nau8825_fll_apply(struct nau8825 *nau8825, NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); /* FLL pre-scaler */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4, - NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div); + NAU8825_FLL_REF_DIV_MASK, + fll_param->clk_ref_div << NAU8825_FLL_REF_DIV_SFT); /* select divided VCO input */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, NAU8825_FLL_FILTER_SW_MASK, 0x0000); diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index dff8edb83bfd..a0b220726a63 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -114,7 +114,8 @@ #define NAU8825_FLL_INTEGER_MASK (0x3ff << 0) /* FLL4 (0x07) */ -#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10) +#define NAU8825_FLL_REF_DIV_SFT 10 +#define NAU8825_FLL_REF_DIV_MASK (0x3 << NAU8825_FLL_REF_DIV_SFT) /* FLL5 (0x08) */ #define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14) diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c index 025a592b4015..cc7132d190b8 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -3662,18 +3662,6 @@ static const struct sdm660_cdc_reg_mask_val {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0xFF, 0xFF}, }; -static void msm_anlg_cdc_codec_init_cache(struct snd_soc_codec *codec) -{ - u32 i; - - regcache_cache_only(codec->component.regmap, true); - /* update cache with POR values */ - for (i = 0; i < ARRAY_SIZE(msm89xx_pmic_cdc_defaults); i++) - snd_soc_write(codec, msm89xx_pmic_cdc_defaults[i].reg, - msm89xx_pmic_cdc_defaults[i].def); - regcache_cache_only(codec->component.regmap, false); -} - static void msm_anlg_cdc_codec_init_reg(struct snd_soc_codec *codec) { u32 i; @@ -3719,7 +3707,7 @@ static struct regulator *msm_anlg_cdc_find_regulator( return sdm660_cdc->supplies[i].consumer; } - dev_err(sdm660_cdc->dev, "Error: regulator not found:%s\n" + dev_dbg(sdm660_cdc->dev, "Error: regulator not found:%s\n" , name); return NULL; } @@ -4171,7 +4159,6 @@ static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec) ARRAY_SIZE(hph_type_detect_controls)); msm_anlg_cdc_bringup(codec); - msm_anlg_cdc_codec_init_cache(codec); msm_anlg_cdc_codec_init_reg(codec); msm_anlg_cdc_update_reg_defaults(codec); diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c index 4249ada17c87..e64956aa2f40 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -1327,6 +1327,9 @@ static const struct snd_soc_dapm_route audio_dig_map[] = { {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, {"RX2 MIX1 INP2", "IIR1", "IIR1"}, {"RX2 MIX1 INP2", "IIR2", "IIR2"}, + {"RX2 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP3", "RX3", "I2S RX3"}, {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, @@ -1338,6 +1341,9 @@ static const struct snd_soc_dapm_route audio_dig_map[] = { {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, {"RX3 MIX1 INP2", "IIR1", "IIR1"}, {"RX3 MIX1 INP2", "IIR2", "IIR2"}, + {"RX3 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP3", "RX3", "I2S RX3"}, {"RX1 MIX2 INP1", "IIR1", "IIR1"}, {"RX2 MIX2 INP1", "IIR1", "IIR1"}, diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index a564759845f9..5a3f544bb3a8 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -126,6 +126,16 @@ static const struct reg_default aic3x_reg[] = { { 108, 0x00 }, { 109, 0x00 }, }; +static bool aic3x_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AIC3X_RESET: + return true; + default: + return false; + } +} + static const struct regmap_config aic3x_regmap = { .reg_bits = 8, .val_bits = 8, @@ -133,6 +143,9 @@ static const struct regmap_config aic3x_regmap = { .max_register = DAC_ICC_ADJ, .reg_defaults = aic3x_reg, .num_reg_defaults = ARRAY_SIZE(aic3x_reg), + + .volatile_reg = aic3x_volatile_reg, + .cache_type = REGCACHE_RBTREE, }; diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c index 9b1c8c98946c..1613c5baa9c7 100644 --- a/sound/soc/codecs/wcd-dsp-mgr.c +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -415,22 +415,24 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, /* Go through the list of segments and download one by one */ list_for_each_entry(seg, wdsp->seg_list, list) { ret = wdsp_load_each_segment(wdsp, seg); - if (IS_ERR_VALUE(ret)) { - wdsp_broadcast_event_downseq(wdsp, - WDSP_EVENT_DLOAD_FAILED, - NULL); + if (ret) goto dload_error; - } } + /* Flush the list before setting status and notifying components */ + wdsp_flush_segment_list(wdsp->seg_list); + WDSP_SET_STATUS(wdsp, status); /* Notify all components that image is downloaded */ wdsp_broadcast_event_downseq(wdsp, post, NULL); +done: + return ret; dload_error: wdsp_flush_segment_list(wdsp->seg_list); -done: + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, NULL); + return ret; } @@ -484,10 +486,14 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) /* Make sure wdsp is in good state */ if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) { WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status); - ret = -EINVAL; - goto done; + return -EINVAL; } + /* + * Acquire SSR mutex lock to make sure enablement of DSP + * does not race with SSR handling. + */ + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); /* Download the read-write sections of image */ ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE); if (IS_ERR_VALUE(ret)) { @@ -508,6 +514,7 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL); WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED); done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); return ret; } diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c index e791bf07ec67..29c218013a07 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -763,10 +763,6 @@ static int wcd_control_handler(struct device *dev, void *priv_data, case WDSP_EVENT_DLOAD_FAILED: case WDSP_EVENT_POST_SHUTDOWN: - if (event == WDSP_EVENT_POST_DLOAD_CODE) - /* Mark DSP online since code download is complete */ - wcd_cntl_change_online_state(cntl, 1); - /* Disable CPAR */ wcd_cntl_cpar_ctrl(cntl, false); /* Disable all the clocks */ @@ -775,6 +771,11 @@ static int wcd_control_handler(struct device *dev, void *priv_data, dev_err(codec->dev, "%s: Failed to disable clocks, err = %d\n", __func__, ret); + + if (event == WDSP_EVENT_POST_DLOAD_CODE) + /* Mark DSP online since code download is complete */ + wcd_cntl_change_online_state(cntl, 1); + break; case WDSP_EVENT_PRE_DLOAD_DATA: diff --git a/sound/soc/msm/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c index b6121d75c148..b1dff8764618 100644 --- a/sound/soc/msm/apq8096-auto.c +++ b/sound/soc/msm/apq8096-auto.c @@ -3044,21 +3044,22 @@ static struct snd_soc_dai_link apq8096_common_dai_links[] = { .codec_name = "snd-soc-dummy", }, { - .name = "SLIMBUS_4 Hostless", - .stream_name = "SLIMBUS_4 Hostless", - .cpu_dai_name = "SLIMBUS4_HOSTLESS", - .platform_name = "msm-pcm-hostless", + .name = "MSM8996 HFP RX", + .stream_name = "MultiMedia21", + .cpu_dai_name = "MultiMedia21", + .platform_name = "msm-pcm-loopback", .dynamic = 1, .dpcm_playback = 1, .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, .ignore_suspend = 1, - /* this dailink has playback support */ + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dainlink has playback support */ .ignore_pmdown_time = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA21, }, { .name = "VoLTE", @@ -3480,12 +3481,13 @@ static struct snd_soc_dai_link apq8096_common_dai_links[] = { .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, { - .name = "MSM8996 Compress9", - .stream_name = "Compress9", + .name = "MSM8996 ULL NOIRQ 2", + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp-noirq", .dynamic = 1, .dpcm_playback = 1, + .dpcm_capture = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .codec_dai_name = "snd-soc-dummy-dai", diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index cc89408fcb39..5facafdc7729 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -2496,8 +2496,21 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .rate_min = 8000, .rate_max = 384000, }, + .capture = { + .stream_name = "MultiMedia16 Capture", + .aif_name = "MM_UL16", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, .ops = &msm_fe_Multimedia_dai_ops, - .compress_new = snd_soc_new_compress, .name = "MultiMedia16", .probe = fe_dai_probe, }, @@ -2656,6 +2669,20 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .rate_min = 8000, .rate_max = 384000, }, + .capture = { + .stream_name = "MultiMedia21 Capture", + .aif_name = "MM_UL21", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, .ops = &msm_fe_Multimedia_dai_ops, .name = "MultiMedia21", .probe = fe_dai_probe, @@ -2736,6 +2763,45 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .name = "MultiMedia25", .probe = fe_dai_probe, }, + { + .playback = { + .stream_name = "MultiMedia26 Playback", + .aif_name = "MM_DL26", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia26", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia27 Capture", + .aif_name = "MM_UL27", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia27", + .probe = fe_dai_probe, + }, }; static int msm_fe_dai_dev_probe(struct platform_device *pdev) diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c index 4eafd322d50d..010dfa3322a0 100644 --- a/sound/soc/msm/msm8996.c +++ b/sound/soc/msm/msm8996.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2910,12 +2910,13 @@ static struct snd_soc_dai_link msm8996_common_dai_links[] = { .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, { - .name = "MSM8996 Compress9", - .stream_name = "Compress9", + .name = "MSM8996 ULL NOIRQ_2", + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp-noirq", .dynamic = 1, .dpcm_playback = 1, + .dpcm_capture = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .codec_dai_name = "snd-soc-dummy-dai", diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c index e715770d909a..9159ea642816 100644 --- a/sound/soc/msm/msm8998.c +++ b/sound/soc/msm/msm8998.c @@ -434,7 +434,8 @@ static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", "KHZ_44P1", "KHZ_48", - "KHZ_96", "KHZ_192"}; + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192"}; static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; @@ -557,6 +558,13 @@ static struct snd_soc_dapm_route wcd_audio_paths[] = { {"MIC BIAS4", NULL, "MCLK"}, }; +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT, +}; + static struct afe_clk_set mi2s_clk[MI2S_MAX] = { { AFE_API_VERSION_I2S_CONFIG, @@ -2221,12 +2229,18 @@ static int mi2s_get_sample_rate_val(int sample_rate) case SAMPLING_RATE_48KHZ: sample_rate_val = 4; break; - case SAMPLING_RATE_96KHZ: + case SAMPLING_RATE_88P2KHZ: sample_rate_val = 5; break; - case SAMPLING_RATE_192KHZ: + case SAMPLING_RATE_96KHZ: sample_rate_val = 6; break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; default: sample_rate_val = 4; break; @@ -2255,9 +2269,15 @@ static int mi2s_get_sample_rate(int value) sample_rate = SAMPLING_RATE_48KHZ; break; case 5: - sample_rate = SAMPLING_RATE_96KHZ; + sample_rate = SAMPLING_RATE_88P2KHZ; break; case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: sample_rate = SAMPLING_RATE_192KHZ; break; default: @@ -4508,6 +4528,11 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) */ mutex_lock(&mi2s_intf_conf[index].lock); if (++mi2s_intf_conf[index].ref_cnt == 1) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + fmt = SND_SOC_DAIFMT_CBM_CFM; + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + } ret = msm_mi2s_set_sclk(substream, true); if (IS_ERR_VALUE(ret)) { dev_err(rtd->card->dev, @@ -4527,9 +4552,6 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) ret = -EINVAL; goto clk_off; } - /* Check if msm needs to provide the clock to the interface */ - if (!mi2s_intf_conf[index].msm_is_mi2s_master) - fmt = SND_SOC_DAIFMT_CBM_CFM; ret = snd_soc_dai_set_fmt(cpu_dai, fmt); if (IS_ERR_VALUE(ret)) { pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", @@ -5306,12 +5328,13 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, { - .name = MSM_DAILINK_NAME(Compress9), - .stream_name = "Compress9", + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp-noirq", .dynamic = 1, .dpcm_playback = 1, + .dpcm_capture = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .codec_dai_name = "snd-soc-dummy-dai", @@ -5500,7 +5523,7 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { { .name = MSM_DAILINK_NAME(Transcode Loopback Playback), .stream_name = "Transcode Loopback Playback", - .cpu_dai_name = "MultiMedia14", + .cpu_dai_name = "MultiMedia26", .platform_name = "msm-transcode-loopback", .dynamic = 1, .dpcm_playback = 1, @@ -5511,12 +5534,12 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dainlink has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA26, }, { .name = MSM_DAILINK_NAME(Transcode Loopback Capture), .stream_name = "Transcode Loopback Capture", - .cpu_dai_name = "MultiMedia18", + .cpu_dai_name = "MultiMedia27", .platform_name = "msm-transcode-loopback", .dynamic = 1, .dpcm_capture = 1, @@ -5526,7 +5549,7 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { .codec_name = "snd-soc-dummy", .ignore_suspend = 1, .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA18, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA27, }, { .name = "MultiMedia21", diff --git a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c index e312a879b86a..1286d3185780 100644 --- a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c @@ -155,7 +155,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "VIRT ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_VIRTUALIZER; *updt_params++ = @@ -183,7 +183,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "VIRT STRENGTH", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_VIRTUALIZER; *updt_params++ = @@ -211,7 +211,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "VIRT OUT_TYPE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_VIRTUALIZER; *updt_params++ = @@ -239,7 +239,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "VIRT GAIN_ADJUST", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_VIRTUALIZER; *updt_params++ = @@ -318,7 +318,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -346,7 +346,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_MODE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -374,7 +374,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_PRESET", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -402,7 +402,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_WET_MIX", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -430,7 +430,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_GAIN_ADJUST", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -458,7 +458,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_ROOM_LEVEL", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -486,7 +486,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_ROOM_HF_LEVEL", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -514,7 +514,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DECAY_TIME", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -542,7 +542,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DECAY_HF_RATIO", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -570,7 +570,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_REFLECTIONS_LEVEL", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -598,7 +598,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_REFLECTIONS_DELAY", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -626,7 +626,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_LEVEL", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -654,7 +654,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DELAY", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -682,7 +682,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DIFFUSION", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -710,7 +710,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DENSITY", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -790,7 +790,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "BASS_BOOST_ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_BASS_BOOST; *updt_params++ = @@ -818,7 +818,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "BASS_BOOST_MODE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_BASS_BOOST; *updt_params++ = @@ -846,7 +846,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "BASS_BOOST_STRENGTH", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_BASS_BOOST; *updt_params++ = @@ -924,7 +924,7 @@ int msm_audio_effects_pbe_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "PBE_ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_PBE; *updt_params++ = @@ -950,7 +950,7 @@ int msm_audio_effects_pbe_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "PBE_PARAM", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_PBE; *updt_params++ = @@ -1035,7 +1035,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "EQ_ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; *updt_params++ = @@ -1103,7 +1103,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "EQ_CONFIG", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; *updt_params++ = @@ -1154,7 +1154,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "EQ_BAND_INDEX", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; *updt_params++ = @@ -1186,7 +1186,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "EQ_SINGLE_BAND_FREQ", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; *updt_params++ = @@ -1276,7 +1276,7 @@ static int __msm_audio_effects_volume_handler(struct audio_client *ac, "VOLUME/VOLUME2_GAIN_2CH", rc); if (rc != 0) - break; + goto invalid_config; if (instance == SOFT_VOLUME_INSTANCE_2) *updt_params++ = ASM_MODULE_ID_VOL_CTRL2; @@ -1325,7 +1325,7 @@ static int __msm_audio_effects_volume_handler(struct audio_client *ac, "VOLUME/VOLUME2_GAIN_MASTER", rc); if (rc != 0) - break; + goto invalid_config; if (instance == SOFT_VOLUME_INSTANCE_2) *updt_params++ = ASM_MODULE_ID_VOL_CTRL2; diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 0acf6e8ffe49..c462f682e160 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -39,6 +39,7 @@ #include <sound/apr_audio-v2.h> #include <sound/q6asm-v2.h> +#include <sound/q6core.h> #include <sound/compress_params.h> #include <sound/compress_offload.h> #include <sound/compress_driver.h> @@ -103,6 +104,7 @@ struct msm_compr_pdata { bool use_legacy_api; /* indicates use older asm apis*/ struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX]; struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX]; + int32_t ion_fd[MSM_FRONTEND_DAI_MAX]; }; struct msm_compr_audio { @@ -156,6 +158,12 @@ struct msm_compr_audio { uint32_t start_delay_lsw; uint32_t start_delay_msw; + int32_t shm_ion_fd; + struct ion_client *lib_ion_client; + struct ion_client *shm_ion_client; + struct ion_handle *lib_ion_handle; + struct ion_handle *shm_ion_handle; + uint64_t marker_timestamp; struct msm_compr_gapless_state gapless_state; @@ -1506,6 +1514,65 @@ static int msm_compr_configure_dsp_for_capture(struct snd_compr_stream *cstream) return ret; } +static int msm_compr_map_ion_fd(struct msm_compr_audio *prtd, int fd) +{ + ion_phys_addr_t paddr; + size_t pa_len = 0; + int ret = 0; + + ret = msm_audio_ion_phys_assign("audio_lib_mem_client", + &prtd->lib_ion_client, + &prtd->lib_ion_handle, + fd, &paddr, &pa_len, HLOS_TO_ADSP); + if (ret) { + pr_err("%s: audio lib ION phys failed, rc = %d\n", + __func__, ret); + goto done; + } + + ret = q6core_add_remove_pool_pages(paddr, pa_len, + ADSP_MEMORY_MAP_HLOS_PHYSPOOL, true); + if (ret) { + pr_err("%s: add remove pages failed, rc = %d\n", __func__, ret); + /* Assign back to HLOS if add pages cmd failed */ + msm_audio_ion_phys_free(prtd->lib_ion_client, + prtd->lib_ion_handle, + &paddr, &pa_len, ADSP_TO_HLOS); + } + +done: + return ret; +} + +static int msm_compr_unmap_ion_fd(struct msm_compr_audio *prtd) +{ + ion_phys_addr_t paddr; + size_t pa_len = 0; + int ret = 0; + + if (!prtd->lib_ion_client || !prtd->lib_ion_handle) { + pr_err("%s: ion_client or ion_handle is NULL", __func__); + return -EINVAL; + } + + ret = msm_audio_ion_phys_free(prtd->lib_ion_client, + prtd->lib_ion_handle, + &paddr, &pa_len, ADSP_TO_HLOS); + if (ret) { + pr_err("%s: audio lib ION phys failed, rc = %d\n", + __func__, ret); + goto done; + } + + ret = q6core_add_remove_pool_pages(paddr, pa_len, + ADSP_MEMORY_MAP_HLOS_PHYSPOOL, false); + if (ret) + pr_err("%s: add remove pages failed, rc = %d\n", __func__, ret); + +done: + return ret; +} + static int msm_compr_playback_open(struct snd_compr_stream *cstream) { struct snd_compr_runtime *runtime = cstream->runtime; @@ -1513,6 +1580,7 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) struct msm_compr_audio *prtd; struct msm_compr_pdata *pdata = snd_soc_platform_get_drvdata(rtd->platform); + int ret = 0; pr_debug("%s\n", __func__); prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL); @@ -1528,19 +1596,16 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL); if (!pdata->audio_effects[rtd->dai_link->be_id]) { pr_err("%s: Could not allocate memory for effects\n", __func__); - pdata->cstream[rtd->dai_link->be_id] = NULL; - kfree(prtd); - return -ENOMEM; + ret = -ENOMEM; + goto effect_err; } pdata->dec_params[rtd->dai_link->be_id] = kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL); if (!pdata->dec_params[rtd->dai_link->be_id]) { pr_err("%s: Could not allocate memory for dec params\n", __func__); - kfree(pdata->audio_effects[rtd->dai_link->be_id]); - pdata->cstream[rtd->dai_link->be_id] = NULL; - kfree(prtd); - return -ENOMEM; + ret = -ENOMEM; + goto param_err; } prtd->codec = FORMAT_MP3; prtd->bytes_received = 0; @@ -1584,19 +1649,32 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) (app_cb)compr_event_handler, prtd); if (!prtd->audio_client) { pr_err("%s: Could not allocate memory for client\n", __func__); - kfree(pdata->audio_effects[rtd->dai_link->be_id]); - kfree(pdata->dec_params[rtd->dai_link->be_id]); - pdata->cstream[rtd->dai_link->be_id] = NULL; - runtime->private_data = NULL; - kfree(prtd); - return -ENOMEM; + ret = -ENOMEM; + goto ac_err; } pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session); prtd->audio_client->perf_mode = false; prtd->session_id = prtd->audio_client->session; msm_adsp_init_mixer_ctl_pp_event_queue(rtd); - + if (pdata->ion_fd[rtd->dai_link->be_id] > 0) { + ret = msm_compr_map_ion_fd(prtd, + pdata->ion_fd[rtd->dai_link->be_id]); + if (ret < 0) + goto map_err; + } return 0; + +map_err: + q6asm_audio_client_free(prtd->audio_client); +ac_err: + kfree(pdata->dec_params[rtd->dai_link->be_id]); +param_err: + kfree(pdata->audio_effects[rtd->dai_link->be_id]); +effect_err: + pdata->cstream[rtd->dai_link->be_id] = NULL; + runtime->private_data = NULL; + kfree(prtd); + return ret; } static int msm_compr_capture_open(struct snd_compr_stream *cstream) @@ -1675,6 +1753,8 @@ static int msm_compr_playback_free(struct snd_compr_stream *cstream) int dir = IN, ret = 0, stream_id; unsigned long flags; uint32_t stream_index; + ion_phys_addr_t paddr; + size_t pa_len = 0; pr_debug("%s\n", __func__); @@ -1748,6 +1828,14 @@ static int msm_compr_playback_free(struct snd_compr_stream *cstream) } q6asm_audio_client_buf_free_contiguous(dir, ac); + if (prtd->shm_ion_fd > 0) + msm_audio_ion_phys_free(prtd->shm_ion_client, + prtd->shm_ion_handle, + &paddr, &pa_len, ADSP_TO_HLOS); + if (pdata->ion_fd[soc_prtd->dai_link->be_id] > 0) { + msm_compr_unmap_ion_fd(prtd); + pdata->ion_fd[soc_prtd->dai_link->be_id] = 0; + } q6asm_audio_client_free(ac); msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); @@ -3655,7 +3743,120 @@ done: return ret; } -static int msm_compr_ion_fd_map_put(struct snd_kcontrol *kcontrol, +static int msm_compr_playback_dnmix_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int len = 0; + int i = 0; + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd; + struct asm_stream_pan_ctrl_params dnmix_param; + int be_id = ucontrol->value.integer.value[len++]; + int stream_id = 0; + /* + * Max index for this mixer control includes below + * be_id (1) + * num_output_channels (1) + * num_input_channels (1) + * output ch map (max) (8) + * input ch map (max) (8) + * mix matrix coefficients (max)(64) + */ + int max_index = 0; + int max_mixer_ctrl_value_size = 128; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + stream_id = prtd->audio_client->session; + if (len >= max_mixer_ctrl_value_size) { + ret = -EINVAL; + goto done; + } + dnmix_param.num_output_channels = + ucontrol->value.integer.value[len++]; + if (dnmix_param.num_output_channels > + PCM_FORMAT_MAX_NUM_CHANNEL) { + ret = -EINVAL; + goto done; + } + if (len >= max_mixer_ctrl_value_size) { + ret = -EINVAL; + goto done; + } + dnmix_param.num_input_channels = + ucontrol->value.integer.value[len++]; + if (dnmix_param.num_input_channels > + PCM_FORMAT_MAX_NUM_CHANNEL) { + ret = -EINVAL; + goto done; + } + max_index = len + dnmix_param.num_output_channels + + dnmix_param.num_input_channels + + dnmix_param.num_output_channels * + dnmix_param.num_input_channels; + if (max_index >= max_mixer_ctrl_value_size) { + ret = -EINVAL; + goto done; + } + + if (ucontrol->value.integer.value[len++]) { + for (i = 0; i < dnmix_param.num_output_channels; i++) { + dnmix_param.output_channel_map[i] = + ucontrol->value.integer.value[len++]; + } + } + if (ucontrol->value.integer.value[len++]) { + for (i = 0; i < dnmix_param.num_input_channels; i++) { + dnmix_param.input_channel_map[i] = + ucontrol->value.integer.value[len++]; + } + } + if (ucontrol->value.integer.value[len++]) { + for (i = 0; i < dnmix_param.num_output_channels * + dnmix_param.num_input_channels; i++) { + dnmix_param.gain[i] = + ucontrol->value.integer.value[len++]; + } + } + msm_routing_set_downmix_control_data(be_id, + stream_id, + &dnmix_param); + +done: + return ret; +} + +static int msm_compr_shm_ion_fd_map_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); @@ -3664,7 +3865,6 @@ static int msm_compr_ion_fd_map_put(struct snd_kcontrol *kcontrol, snd_soc_component_get_drvdata(comp); struct snd_compr_stream *cstream = NULL; struct msm_compr_audio *prtd; - int fd; int ret = 0; if (fe_id >= MSM_FRONTEND_DAI_MAX) { @@ -3694,10 +3894,36 @@ static int msm_compr_ion_fd_map_put(struct snd_kcontrol *kcontrol, goto done; } - memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd)); - ret = q6asm_send_ion_fd(prtd->audio_client, fd); + memcpy(&prtd->shm_ion_fd, ucontrol->value.bytes.data, + sizeof(prtd->shm_ion_fd)); + ret = q6asm_audio_map_shm_fd(prtd->audio_client, + &prtd->shm_ion_client, + &prtd->shm_ion_handle, prtd->shm_ion_fd); if (ret < 0) - pr_err("%s: failed to register ion fd\n", __func__); + pr_err("%s: failed to map shm mem\n", __func__); +done: + return ret; +} + +static int msm_compr_lib_ion_fd_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + int ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + memcpy(&pdata->ion_fd[fe_id], ucontrol->value.bytes.data, + sizeof(pdata->ion_fd[fe_id])); + done: return ret; } @@ -3901,6 +4127,16 @@ static int msm_compr_channel_map_info(struct snd_kcontrol *kcontrol, return 0; } +static int msm_compr_device_downmix_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 128; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd) { const char *mixer_ctl_name = "Compress Playback"; @@ -4199,6 +4435,53 @@ static int msm_compr_add_dec_runtime_params_control( return 0; } +static int msm_compr_add_device_down_mix_controls( + struct snd_soc_pcm_runtime *rtd) +{ + const char *playback_mixer_ctl_name = "Audio Device"; + const char *deviceNo = "NN"; + const char *suffix = "Downmix Control"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new device_downmix_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_device_downmix_info, + .put = msm_compr_playback_dnmix_ctl_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + ctl_len = strlen(playback_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d %s", + playback_mixer_ctl_name, rtd->pcm->device, suffix); + device_downmix_control[0].name = mixer_str; + device_downmix_control[0].private_value = rtd->dai_link->be_id; + ret = snd_soc_add_platform_controls(rtd->platform, + device_downmix_control, + ARRAY_SIZE(device_downmix_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd) { const char *playback_mixer_ctl_name = "Audio Stream"; @@ -4329,7 +4612,7 @@ static int msm_compr_add_channel_map_control(struct snd_soc_pcm_runtime *rtd) return 0; } -static int msm_compr_add_io_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) +static int msm_compr_add_shm_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) { const char *mixer_ctl_name = "Playback ION FD"; const char *deviceNo = "NN"; @@ -4341,7 +4624,52 @@ static int msm_compr_add_io_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) .name = "?", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = msm_adsp_stream_cmd_info, - .put = msm_compr_ion_fd_map_put, + .put = msm_compr_shm_ion_fd_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_ion_fd_config_control[0].name = mixer_str; + fe_ion_fd_config_control[0].private_value = rtd->dai_link->be_id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_ion_fd_config_control, + ARRAY_SIZE(fe_ion_fd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_add_lib_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback ION LIB FD"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_ion_fd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_compr_lib_ion_fd_map_put, .private_value = 0, } }; @@ -4442,16 +4770,26 @@ static int msm_compr_new(struct snd_soc_pcm_runtime *rtd) pr_err("%s: Could not add Compr ADSP Stream Callback Control\n", __func__); - rc = msm_compr_add_io_fd_cmd_control(rtd); + rc = msm_compr_add_shm_ion_fd_cmd_control(rtd); if (rc) pr_err("%s: Could not add Compr ion fd Control\n", __func__); + rc = msm_compr_add_lib_ion_fd_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add Compr ion lib fd Control\n", + __func__); + rc = msm_compr_add_event_ack_cmd_control(rtd); if (rc) pr_err("%s: Could not add Compr event ack Control\n", __func__); + rc = msm_compr_add_device_down_mix_controls(rtd); + if (rc) + pr_err("%s: Could not add Compr downmix Control\n", + __func__); + rc = msm_compr_add_query_audio_effect_control(rtd); if (rc) pr_err("%s: Could not add Compr Query Audio Effect Control\n", diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 3610901addea..292b3d04f7d5 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -4152,7 +4152,8 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, .rate_min = 8000, diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c index 3e72aa130c18..35270e3340ec 100644 --- a/sound/soc/msm/qdsp6v2/msm-lsm-client.c +++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c @@ -1683,7 +1683,7 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s REG_SND_MODEL failed err %d\n", __func__, err); - return err; + goto done; } break; case SNDRV_LSM_SET_PARAMS: { @@ -1855,13 +1855,15 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: Invalid params event_status_v3\n", __func__); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&userarg, arg, sizeof(userarg))) { dev_err(rtd->dev, "%s: err copyuser event_status_v3\n", __func__); - return -EFAULT; + err = -EFAULT; + goto done; } if (userarg.payload_size > @@ -1869,7 +1871,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, pr_err("%s: payload_size %d is invalid, max allowed = %d\n", __func__, userarg.payload_size, LISTEN_MAX_STATUS_PAYLOAD_SIZE); - return -EINVAL; + err = -EINVAL; + goto done; } size = sizeof(struct snd_lsm_event_status_v3) + @@ -1879,7 +1882,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: Allocation failed event status size %d\n", __func__, size); - return -EFAULT; + err = -EFAULT; + goto done; } user->payload_size = userarg.payload_size; err = msm_lsm_ioctl_shared(substream, cmd, user); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c index de769e8b806c..b94eb6fbfeea 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -1668,7 +1668,7 @@ static int msm_pcm_playback_pan_scale_ctl_put(struct snd_kcontrol *kcontrol, for (i = 0; i < pan_param.num_output_channels * pan_param.num_input_channels; i++) { pan_param.gain[i] = - !(ucontrol->value.integer.value[len++] > 0) ? + !(ucontrol->value.integer.value[len++] > 0) ? 0 : 2 << 13; } } @@ -1679,8 +1679,12 @@ static int msm_pcm_playback_pan_scale_ctl_put(struct snd_kcontrol *kcontrol, pan_param.num_input_channels; for (i = 0; i < pan_param.num_output_channels * pan_param.num_input_channels; i++) { + /* + * The data userspace passes is already in Q14 format. + * For volume gain is in Q28. + */ pan_param.gain[i] = - ucontrol->value.integer.value[len++]; + ucontrol->value.integer.value[len++] << 14; } ret = q6asm_set_vol_ctrl_gain_pair(prtd->audio_client, &pan_param); @@ -1753,7 +1757,7 @@ static int msm_pcm_playback_dnmix_ctl_put(struct snd_kcontrol *kcontrol, struct msm_audio *prtd; struct asm_stream_pan_ctrl_params dnmix_param; - int be_id = ucontrol->value.integer.value[len]; + int be_id = ucontrol->value.integer.value[len++]; int stream_id = 0; if (!usr_info) { @@ -1809,7 +1813,7 @@ static int msm_pcm_playback_dnmix_ctl_put(struct snd_kcontrol *kcontrol, for (i = 0; i < dnmix_param.num_output_channels * dnmix_param.num_input_channels; i++) { dnmix_param.gain[i] = - ucontrol->value.integer.value[len++]; + ucontrol->value.integer.value[len++]; } } msm_routing_set_downmix_control_data(be_id, diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 2bf61521ad52..2114adae72d7 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -605,6 +605,27 @@ static struct msm_pcm_routing_fdai_data /* MULTIMEDIA20 */ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA21 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA22 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA23 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA24 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA25 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA26 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA27 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* CS_VOICE */ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, @@ -3654,6 +3675,11 @@ static const struct snd_kcontrol_new ext_ec_ref_mux_ul9 = msm_route_ec_ref_rx_enum[0], msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); +static const struct snd_kcontrol_new ext_ec_ref_mux_ul16 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL16 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + static const struct snd_kcontrol_new ext_ec_ref_mux_ul17 = SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL17 MUX Mux", msm_route_ec_ref_rx_enum[0], @@ -3812,6 +3838,9 @@ static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_I2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = { @@ -3872,6 +3901,9 @@ static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SEC_I2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = { @@ -3932,7 +3964,9 @@ static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SPDIF_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), - + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = { @@ -3984,6 +4018,9 @@ static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_2_RX, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = { @@ -4044,6 +4081,9 @@ static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SLIMBUS_5_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { @@ -4119,6 +4159,9 @@ static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia25", MSM_BACKEND_DAI_SLIMBUS_0_RX, MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = { @@ -4179,6 +4222,9 @@ static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = { @@ -4239,6 +4285,9 @@ static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = { @@ -4299,6 +4348,9 @@ static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUINARY_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = { @@ -4353,6 +4405,9 @@ static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new secondary_mi2s_rx2_mixer_controls[] = { @@ -4419,6 +4474,9 @@ static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { @@ -4479,6 +4537,9 @@ static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new int0_mi2s_rx_mixer_controls[] = { @@ -4530,6 +4591,9 @@ static const struct snd_kcontrol_new int0_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT0_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new int4_mi2s_rx_mixer_controls[] = { @@ -4581,6 +4645,9 @@ static const struct snd_kcontrol_new int4_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT4_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new hdmi_mixer_controls[] = { @@ -4656,6 +4723,9 @@ static const struct snd_kcontrol_new hdmi_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia25", MSM_BACKEND_DAI_HDMI_RX, MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new display_port_mixer_controls[] = { @@ -4707,6 +4777,9 @@ static const struct snd_kcontrol_new display_port_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_DISPLAY_PORT_RX, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; /* incall music delivery mixer */ @@ -4819,6 +4892,9 @@ static const struct snd_kcontrol_new slimbus_6_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia25", MSM_BACKEND_DAI_SLIMBUS_6_RX, MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new slimbus_7_rx_mixer_controls[] = { @@ -4885,6 +4961,9 @@ static const struct snd_kcontrol_new slimbus_7_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia25", MSM_BACKEND_DAI_SLIMBUS_7_RX, MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new usb_audio_rx_mixer_controls[] = { @@ -4936,6 +5015,9 @@ static const struct snd_kcontrol_new usb_audio_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_USB_RX, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = { @@ -4996,6 +5078,9 @@ static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_BT_SCO_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = { @@ -5047,6 +5132,9 @@ static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT_BT_A2DP_RX, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = { @@ -5107,6 +5195,9 @@ static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_FM_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { @@ -5167,6 +5258,9 @@ static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_AFE_PCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { @@ -5227,6 +5321,12 @@ static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_AUXPCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = { @@ -5287,6 +5387,12 @@ static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SEC_AUXPCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = { @@ -5338,6 +5444,9 @@ static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_AUXPCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = { @@ -5389,6 +5498,9 @@ static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = { @@ -5440,6 +5552,12 @@ static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = { @@ -5491,6 +5609,12 @@ static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = { @@ -5542,6 +5666,12 @@ static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = { @@ -5593,6 +5723,12 @@ static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_tx_0_mixer_controls[] = { @@ -5695,6 +5831,12 @@ static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = { @@ -5746,6 +5888,12 @@ static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = { @@ -5797,6 +5945,12 @@ static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = { @@ -5848,6 +6002,12 @@ static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_tdm_tx_0_mixer_controls[] = { @@ -5950,6 +6110,12 @@ static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_tx_0_mixer_controls[] = { @@ -6052,6 +6218,12 @@ static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = { @@ -6103,6 +6275,12 @@ static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = { @@ -6154,6 +6332,12 @@ static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = { @@ -6205,6 +6389,12 @@ static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_4, MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = { @@ -6259,6 +6449,12 @@ static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = { @@ -6364,6 +6560,12 @@ static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = { @@ -6418,6 +6620,12 @@ static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { @@ -6472,6 +6680,12 @@ static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new mmul1_mixer_controls[] = { @@ -7161,6 +7375,114 @@ static const struct snd_kcontrol_new mmul8_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new mmul16_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_6_TX", MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + static const struct snd_kcontrol_new mmul9_mixer_controls[] = { SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, @@ -7350,6 +7672,75 @@ static const struct snd_kcontrol_new mmul20_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new mmul21_mixer_controls[] = { + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul27_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = { SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, @@ -11457,6 +11848,7 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_AIF_IN("MM_DL23", "MultiMedia23 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("MM_DL24", "MultiMedia24 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("MM_DL25", "MultiMedia25 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL26", "MultiMedia26 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0), @@ -11466,10 +11858,13 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL9", "MultiMedia9 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL16", "MultiMedia16 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL17", "MultiMedia17 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL18", "MultiMedia18 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL19", "MultiMedia19 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL20", "MultiMedia20 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL21", "MultiMedia21 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL27", "MultiMedia27 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("VOICE2_DL", "Voice2 Playback", 0, 0, 0, 0), @@ -11816,8 +12211,16 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("INT0_MI2S_RX", "INT0 MI2S Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT2_MI2S_RX", "INT2 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT3_MI2S_RX", "INT3 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT5_MI2S_RX", "INT5 MI2S Playback", + 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("INT4_MI2S_RX", "INT4 MI2S Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT4_MI2S_TX", "INT4 MI2S Capture", + 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_RX", "Quinary MI2S Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0), @@ -11828,6 +12231,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT0_MI2S_TX", "INT0 MI2S Capture", + 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("INT2_MI2S_TX", "INT2 MI2S Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("INT3_MI2S_TX", "INT3 MI2S Capture", @@ -12202,6 +12607,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia9 Mixer", SND_SOC_NOPM, 0, 0, mmul9_mixer_controls, ARRAY_SIZE(mmul9_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia16 Mixer", SND_SOC_NOPM, 0, 0, + mmul16_mixer_controls, ARRAY_SIZE(mmul16_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia17 Mixer", SND_SOC_NOPM, 0, 0, mmul17_mixer_controls, ARRAY_SIZE(mmul17_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia18 Mixer", SND_SOC_NOPM, 0, 0, @@ -12210,6 +12617,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { mmul19_mixer_controls, ARRAY_SIZE(mmul19_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia20 Mixer", SND_SOC_NOPM, 0, 0, mmul20_mixer_controls, ARRAY_SIZE(mmul20_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia21 Mixer", SND_SOC_NOPM, 0, 0, + mmul21_mixer_controls, ARRAY_SIZE(mmul21_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia27 Mixer", SND_SOC_NOPM, 0, 0, + mmul27_mixer_controls, ARRAY_SIZE(mmul27_mixer_controls)), SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)), SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, @@ -12532,6 +12943,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { &ext_ec_ref_mux_ul8), SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL9 MUX", SND_SOC_NOPM, 0, 0, &ext_ec_ref_mux_ul9), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL16 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul16), SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL17 MUX", SND_SOC_NOPM, 0, 0, &ext_ec_ref_mux_ul17), SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL18 MUX", SND_SOC_NOPM, 0, 0, @@ -12557,6 +12970,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"PRI_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"PRI_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"}, {"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12575,6 +12989,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SEC_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SEC_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"}, {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12598,6 +13013,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIMBUS_0_RX Audio Mixer", "MultiMedia23", "MM_DL23"}, {"SLIMBUS_0_RX Audio Mixer", "MultiMedia24", "MM_DL24"}, {"SLIMBUS_0_RX Audio Mixer", "MultiMedia25", "MM_DL25"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"}, {"SLIMBUS_2_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12616,6 +13032,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIMBUS_2_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SLIMBUS_2_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SLIMBUS_2_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SLIMBUS_2_RX", NULL, "SLIMBUS_2_RX Audio Mixer"}, {"SLIMBUS_5_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12634,6 +13051,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIMBUS_5_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SLIMBUS_5_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SLIMBUS_5_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SLIMBUS_5_RX", NULL, "SLIMBUS_5_RX Audio Mixer"}, {"HDMI Mixer", "MultiMedia1", "MM_DL1"}, @@ -12657,6 +13075,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"HDMI Mixer", "MultiMedia23", "MM_DL23"}, {"HDMI Mixer", "MultiMedia24", "MM_DL24"}, {"HDMI Mixer", "MultiMedia25", "MM_DL25"}, + {"HDMI Mixer", "MultiMedia26", "MM_DL26"}, {"HDMI", NULL, "HDMI Mixer"}, {"DISPLAY_PORT Mixer", "MultiMedia1", "MM_DL1"}, @@ -12675,6 +13094,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"DISPLAY_PORT Mixer", "MultiMedia14", "MM_DL14"}, {"DISPLAY_PORT Mixer", "MultiMedia15", "MM_DL15"}, {"DISPLAY_PORT Mixer", "MultiMedia16", "MM_DL16"}, + {"DISPLAY_PORT Mixer", "MultiMedia26", "MM_DL26"}, {"DISPLAY_PORT", NULL, "DISPLAY_PORT Mixer"}, {"SPDIF_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12693,6 +13113,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SPDIF_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SPDIF_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SPDIF_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SPDIF_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SPDIF_RX", NULL, "SPDIF_RX Audio Mixer"}, /* incall */ @@ -12733,6 +13154,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIMBUS_6_RX Audio Mixer", "MultiMedia23", "MM_DL23"}, {"SLIMBUS_6_RX Audio Mixer", "MultiMedia24", "MM_DL24"}, {"SLIMBUS_6_RX Audio Mixer", "MultiMedia25", "MM_DL25"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Audio Mixer"}, {"SLIMBUS_7_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12756,6 +13178,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIMBUS_7_RX Audio Mixer", "MultiMedia23", "MM_DL23"}, {"SLIMBUS_7_RX Audio Mixer", "MultiMedia24", "MM_DL24"}, {"SLIMBUS_7_RX Audio Mixer", "MultiMedia25", "MM_DL25"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SLIMBUS_7_RX", NULL, "SLIMBUS_7_RX Audio Mixer"}, {"USB_AUDIO_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12774,6 +13197,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"USB_AUDIO_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"USB_AUDIO_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"USB_AUDIO_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX Audio Mixer"}, {"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"}, @@ -12803,6 +13227,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia8 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia16 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia5 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, {"MultiMedia5 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12821,6 +13246,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"MI2S_RX", NULL, "MI2S_RX Audio Mixer"}, {"QUAT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12838,6 +13264,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"QUAT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Audio Mixer"}, {"TERT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12854,6 +13281,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"TERT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"TERT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Audio Mixer"}, {"SEC_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12870,6 +13298,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SEC_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SEC_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Audio Mixer"}, {"SEC_MI2S_RX_SD1 Audio Mixer", "MultiMedia6", "MM_DL6"}, @@ -12893,6 +13322,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"PRI_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"PRI_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Audio Mixer"}, {"INT0_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12910,6 +13340,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"INT0_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"INT0_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"INT0_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX Audio Mixer"}, {"INT4_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12927,6 +13358,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"INT4_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"INT4_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"INT4_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX Audio Mixer"}, {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12946,6 +13378,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"QUIN_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUIN_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX Audio Mixer"}, {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12964,6 +13397,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"}, {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -12982,6 +13417,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Audio Mixer"}, {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13000,6 +13437,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Audio Mixer"}, {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13018,6 +13457,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 Audio Mixer"}, {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13054,6 +13495,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"}, {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13072,6 +13515,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Audio Mixer"}, {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13090,6 +13535,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Audio Mixer"}, {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13108,6 +13555,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 Audio Mixer"}, {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13144,6 +13593,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Audio Mixer"}, {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13180,6 +13631,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Audio Mixer"}, {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13198,6 +13651,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Audio Mixer"}, {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13216,6 +13671,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Audio Mixer"}, {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13234,6 +13691,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"TERT_TDM_RX_4", NULL, "TERT_TDM_RX_4 Audio Mixer"}, {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13253,6 +13712,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Audio Mixer"}, {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13271,6 +13732,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"}, {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13289,6 +13751,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"}, {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13326,6 +13789,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Audio Mixer"}, {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13345,6 +13810,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Audio Mixer"}, {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13364,6 +13831,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"}, {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"}, {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"}, @@ -13371,9 +13840,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia3 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia16 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"MultiMedia2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"MultiMedia6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia27 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"MultiMedia1 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, {"MultiMedia2 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, {"MultiMedia1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, @@ -13383,24 +13854,29 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia27 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, {"MultiMedia3 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"}, {"MultiMedia5 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, {"MultiMedia1 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, {"MultiMedia3 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, {"MultiMedia5 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia16 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"}, + {"MultiMedia16 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, {"MultiMedia1 Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"}, {"MultiMedia3 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"}, {"MultiMedia5 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"}, {"MultiMedia1 Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"}, {"MultiMedia3 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, {"MultiMedia5 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia16 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, {"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia2 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, {"MultiMedia2 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, {"MultiMedia2 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"MultiMedia1 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia27 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia2 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"MultiMedia6 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia6 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, @@ -13409,9 +13885,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia6 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, {"MultiMedia3 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, {"MultiMedia5 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia16 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, {"MultiMedia6 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia16 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia6 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia6 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, {"MultiMedia6 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, @@ -13566,6 +14044,25 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia20 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"MultiMedia20 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia21 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia21 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia21 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia21 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia21 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia21 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia21 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia21 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia21 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia21 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia21 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia21 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia21 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia21 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia21 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia21 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia21 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"MultiMedia21 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia1 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, {"MultiMedia2 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, {"MultiMedia4 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, @@ -13573,6 +14070,24 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia6 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, {"MultiMedia8 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia16 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, @@ -13655,8 +14170,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia19 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, {"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, {"MultiMedia8 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia16 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia4 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia16 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia17 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia18 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia19 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, @@ -13672,6 +14189,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia19 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia8 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia16 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MM_UL1", NULL, "MultiMedia1 Mixer"}, {"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MM_UL2", NULL, "MultiMedia2 Mixer"}, @@ -13681,10 +14199,13 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL6", NULL, "MultiMedia6 Mixer"}, {"MM_UL8", NULL, "MultiMedia8 Mixer"}, {"MM_UL9", NULL, "MultiMedia9 Mixer"}, + {"MM_UL16", NULL, "MultiMedia16 Mixer"}, {"MM_UL17", NULL, "MultiMedia17 Mixer"}, {"MM_UL18", NULL, "MultiMedia18 Mixer"}, {"MM_UL19", NULL, "MultiMedia19 Mixer"}, {"MM_UL20", NULL, "MultiMedia20 Mixer"}, + {"MM_UL21", NULL, "MultiMedia21 Mixer"}, + {"MM_UL27", NULL, "MultiMedia27 Mixer"}, {"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, {"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, @@ -13702,6 +14223,9 @@ static const struct snd_soc_dapm_route intercon[] = { {"AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_UL6"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_UL21"}, {"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"}, {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -13720,6 +14244,9 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_UL6"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_UL21"}, {"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX Audio Mixer"}, {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -14091,6 +14618,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL6", NULL, "AUDIO_REF_EC_UL6 MUX"}, {"MM_UL8", NULL, "AUDIO_REF_EC_UL8 MUX"}, {"MM_UL9", NULL, "AUDIO_REF_EC_UL9 MUX"}, + {"MM_UL16", NULL, "AUDIO_REF_EC_UL16 MUX"}, {"MM_UL17", NULL, "AUDIO_REF_EC_UL17 MUX"}, {"MM_UL18", NULL, "AUDIO_REF_EC_UL18 MUX"}, {"MM_UL19", NULL, "AUDIO_REF_EC_UL19 MUX"}, @@ -14967,6 +15495,9 @@ static const struct snd_soc_dapm_route intercon[] = { {"BE_OUT", NULL, "PRI_MI2S_RX"}, {"BE_OUT", NULL, "INT0_MI2S_RX"}, {"BE_OUT", NULL, "INT4_MI2S_RX"}, + {"BE_OUT", NULL, "INT2_MI2S_RX"}, + {"BE_OUT", NULL, "INT3_MI2S_RX"}, + {"BE_OUT", NULL, "INT5_MI2S_RX"}, {"BE_OUT", NULL, "INT_BT_SCO_RX"}, {"BE_OUT", NULL, "INT_BT_A2DP_RX"}, {"BE_OUT", NULL, "INT_FM_RX"}, @@ -15006,8 +15537,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_MI2S_TX", NULL, "BE_IN"}, {"PRI_MI2S_TX", NULL, "BE_IN"}, {"TERT_MI2S_TX", NULL, "BE_IN"}, + {"INT0_MI2S_TX", NULL, "BE_IN"}, {"INT2_MI2S_TX", NULL, "BE_IN"}, {"INT3_MI2S_TX", NULL, "BE_IN"}, + {"INT4_MI2S_TX", NULL, "BE_IN"}, {"INT5_MI2S_TX", NULL, "BE_IN"}, {"SEC_MI2S_TX", NULL, "BE_IN"}, {"SENARY_MI2S_TX", NULL, "BE_IN" }, @@ -15141,10 +15674,6 @@ static int msm_pcm_routing_close(struct snd_pcm_substream *substream) bedai->active = 0; bedai->sample_rate = 0; bedai->channel = 0; - for (i = 0; i < MSM_FRONTEND_DAI_MAX; i++) { - if (bedai->passthr_mode[i] != LISTEN) - bedai->passthr_mode[i] = LEGACY_PCM; - } mutex_unlock(&routing_lock); return 0; @@ -15683,6 +16212,8 @@ int msm_routing_set_downmix_control_data(int be_id, int session_id, char *adm_params = NULL; int port_id, copp_idx = 0; uint32_t params_length = 0; + uint16_t ii; + uint16_t *dst_gain_ptr = NULL; if (be_id >= MSM_BACKEND_DAI_MAX) { rc = -EINVAL; @@ -15695,7 +16226,7 @@ int msm_routing_set_downmix_control_data(int be_id, int session_id, variable_payload = dnmix_param->num_output_channels * sizeof(uint16_t)+ dnmix_param->num_input_channels * sizeof(uint16_t) + - dnmix_param->num_output_channels * sizeof(uint16_t) * + dnmix_param->num_output_channels * dnmix_param->num_input_channels * sizeof(uint16_t); i = (variable_payload % sizeof(uint32_t)); variable_payload += (i == 0) ? 0 : sizeof(uint32_t) - i; @@ -15734,14 +16265,15 @@ int msm_routing_set_downmix_control_data(int be_id, int session_id, dnmix_param->num_output_channels * sizeof(uint16_t)), dnmix_param->input_channel_map, dnmix_param->num_input_channels * sizeof(uint16_t)); - memcpy(((u8 *)adm_params + + + dst_gain_ptr = (uint16_t *) ((u8 *)adm_params + sizeof(struct adm_pspd_param_data_t) + sizeof(struct audproc_chmixer_param_coeff) + (dnmix_param->num_output_channels * sizeof(uint16_t)) + - (dnmix_param->num_input_channels * sizeof(uint16_t))), - dnmix_param->gain, - (dnmix_param->num_output_channels * sizeof(uint16_t)) * (dnmix_param->num_input_channels * sizeof(uint16_t))); + for (ii = 0; ii < dnmix_param->num_output_channels * + dnmix_param->num_input_channels; ii++) + dst_gain_ptr[ii] = (uint16_t) dnmix_param->gain[ii]; if (params_length) { rc = adm_set_pspd_matrix_params(port_id, diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h index a8daff09db58..21dfa48308c3 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h @@ -56,8 +56,8 @@ #define LPASS_BE_SEC_MI2S_TX "SEC_MI2S_TX" #define LPASS_BE_PRI_MI2S_RX "PRI_MI2S_RX" #define LPASS_BE_PRI_MI2S_TX "PRI_MI2S_TX" -#define LPASS_BE_TERT_MI2S_RX "TERTIARY_MI2S_RX" -#define LPASS_BE_TERT_MI2S_TX "TERTIARY_MI2S_TX" +#define LPASS_BE_TERT_MI2S_RX "TERT_MI2S_RX" +#define LPASS_BE_TERT_MI2S_TX "TERT_MI2S_TX" #define LPASS_BE_AUDIO_I2S_RX "AUDIO_I2S_RX" #define LPASS_BE_STUB_RX "STUB_RX" #define LPASS_BE_STUB_TX "STUB_TX" @@ -198,6 +198,8 @@ enum { MSM_FRONTEND_DAI_MULTIMEDIA23, MSM_FRONTEND_DAI_MULTIMEDIA24, MSM_FRONTEND_DAI_MULTIMEDIA25, + MSM_FRONTEND_DAI_MULTIMEDIA26, + MSM_FRONTEND_DAI_MULTIMEDIA27, MSM_FRONTEND_DAI_CS_VOICE, MSM_FRONTEND_DAI_VOIP, MSM_FRONTEND_DAI_AFE_RX, @@ -223,8 +225,8 @@ enum { MSM_FRONTEND_DAI_MAX, }; -#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA25 + 1) -#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA25 +#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA27 + 1) +#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA27 enum { MSM_BACKEND_DAI_PRI_I2S_RX = 0, diff --git a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c index 6bb85ca8e84e..18cac3424054 100644 --- a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c @@ -27,6 +27,8 @@ #include <sound/initval.h> #include <sound/control.h> #include <sound/q6asm-v2.h> +#include <sound/q6core.h> +#include <sound/q6audio-v2.h> #include <sound/pcm_params.h> #include <sound/timer.h> #include <sound/tlv.h> @@ -40,11 +42,21 @@ #include "msm-qti-pp-config.h" #define LOOPBACK_SESSION_MAX_NUM_STREAMS 2 +/* Max volume corresponding to 24dB */ +#define TRANSCODE_LR_VOL_MAX_STEPS 0xFFFF + +#define APP_TYPE_CONFIG_IDX_APP_TYPE 0 +#define APP_TYPE_CONFIG_IDX_ACDB_ID 1 +#define APP_TYPE_CONFIG_IDX_SAMPLE_RATE 2 +#define APP_TYPE_CONFIG_IDX_BE_ID 3 static DEFINE_MUTEX(transcode_loopback_session_lock); struct trans_loopback_pdata { struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX]; + int32_t ion_fd[MSM_FRONTEND_DAI_MAX]; + uint32_t master_gain; + int perf_mode; }; struct loopback_stream { @@ -79,6 +91,11 @@ struct msm_transcode_loopback { int session_id; struct audio_client *audio_client; + int32_t shm_ion_fd; + struct ion_client *lib_ion_client; + struct ion_client *shm_ion_client; + struct ion_handle *lib_ion_handle; + struct ion_handle *shm_ion_handle; }; /* Transcode loopback global info struct */ @@ -100,7 +117,7 @@ static void loopback_event_handler(uint32_t opcode, return; } - cstream = trans->source.cstream; + cstream = trans->sink.cstream; ac = trans->audio_client; /* @@ -179,6 +196,65 @@ static void populate_codec_list(struct msm_transcode_loopback *trans, } } +static int msm_transcode_map_ion_fd(struct msm_transcode_loopback *trans, + int fd) +{ + ion_phys_addr_t paddr; + size_t pa_len = 0; + int ret = 0; + + ret = msm_audio_ion_phys_assign("audio_lib_mem_client", + &trans->lib_ion_client, + &trans->lib_ion_handle, fd, + &paddr, &pa_len, HLOS_TO_ADSP); + if (ret) { + pr_err("%s: audio lib ION phys failed, rc = %d\n", __func__, + ret); + goto done; + } + + ret = q6core_add_remove_pool_pages(paddr, pa_len, + ADSP_MEMORY_MAP_HLOS_PHYSPOOL, true); + if (ret) { + pr_err("%s: add pages failed, rc = %d\n", __func__, ret); + /* Assign back to HLOS if add pages cmd failed */ + msm_audio_ion_phys_free(trans->lib_ion_client, + trans->lib_ion_handle, + &paddr, &pa_len, ADSP_TO_HLOS); + } + +done: + return ret; +} + +static int msm_transcode_unmap_ion_fd(struct msm_transcode_loopback *trans) +{ + ion_phys_addr_t paddr; + size_t pa_len = 0; + int ret = 0; + + if (!trans->lib_ion_client || !trans->lib_ion_handle) { + pr_err("%s: ion_client or ion_handle is NULL", __func__); + return -EINVAL; + } + ret = msm_audio_ion_phys_free(trans->lib_ion_client, + trans->lib_ion_handle, + &paddr, &pa_len, ADSP_TO_HLOS); + if (ret) { + pr_err("%s: audio lib ION phys failed, rc = %d\n", __func__, + ret); + goto done; + } + + ret = q6core_add_remove_pool_pages(paddr, pa_len, + ADSP_MEMORY_MAP_HLOS_PHYSPOOL, false); + if (ret) + pr_err("%s: remove pages failed, rc = %d\n", __func__, ret); + +done: + return ret; +} + static int msm_transcode_loopback_open(struct snd_compr_stream *cstream) { int ret = 0; @@ -223,6 +299,13 @@ static int msm_transcode_loopback_open(struct snd_compr_stream *cstream) ret = -EINVAL; goto exit; } + msm_adsp_init_mixer_ctl_pp_event_queue(rtd); + if (pdata->ion_fd[rtd->dai_link->be_id] > 0) { + ret = msm_transcode_map_ion_fd(trans, + pdata->ion_fd[rtd->dai_link->be_id]); + if (ret < 0) + goto exit; + } } pr_debug("%s: num stream%d, stream name %s\n", __func__, @@ -237,8 +320,7 @@ static int msm_transcode_loopback_open(struct snd_compr_stream *cstream) } runtime->private_data = trans; - if (trans->num_streams == 1) - msm_adsp_init_mixer_ctl_pp_event_queue(rtd); + exit: mutex_unlock(&trans->lock); return ret; @@ -274,7 +356,11 @@ static int msm_transcode_loopback_free(struct snd_compr_stream *cstream) struct snd_compr_runtime *runtime = cstream->runtime; struct msm_transcode_loopback *trans = runtime->private_data; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(cstream); + struct trans_loopback_pdata *pdata = snd_soc_platform_get_drvdata( + rtd->platform); int ret = 0; + ion_phys_addr_t paddr; + size_t pa_len = 0; mutex_lock(&trans->lock); @@ -283,14 +369,24 @@ static int msm_transcode_loopback_free(struct snd_compr_stream *cstream) trans->num_streams--; stop_transcoding(trans); - if (cstream->direction == SND_COMPRESS_PLAYBACK) + if (cstream->direction == SND_COMPRESS_PLAYBACK) { memset(&trans->sink, 0, sizeof(struct loopback_stream)); - else if (cstream->direction == SND_COMPRESS_CAPTURE) + msm_adsp_clean_mixer_ctl_pp_event_queue(rtd); + if (trans->shm_ion_fd > 0) { + msm_audio_ion_phys_free(trans->shm_ion_client, + trans->shm_ion_handle, + &paddr, &pa_len, ADSP_TO_HLOS); + trans->shm_ion_fd = 0; + } + if (pdata->ion_fd[rtd->dai_link->be_id] > 0) { + msm_transcode_unmap_ion_fd(trans); + pdata->ion_fd[rtd->dai_link->be_id] = 0; + } + } else if (cstream->direction == SND_COMPRESS_CAPTURE) { memset(&trans->source, 0, sizeof(struct loopback_stream)); + } trans->session_state = LOOPBACK_SESSION_CLOSE; - if (trans->num_streams == 1) - msm_adsp_clean_mixer_ctl_pp_event_queue(rtd); mutex_unlock(&trans->lock); return ret; } @@ -337,6 +433,8 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream, struct msm_transcode_loopback *trans = runtime->private_data; struct snd_soc_pcm_runtime *soc_pcm_rx; struct snd_soc_pcm_runtime *soc_pcm_tx; + struct snd_soc_pcm_runtime *rtd; + struct trans_loopback_pdata *pdata; uint32_t bit_width = 16; int ret = 0; @@ -347,6 +445,9 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream, mutex_lock(&trans->lock); + rtd = snd_pcm_substream_chip(cstream); + pdata = snd_soc_platform_get_drvdata(rtd->platform); + if (cstream->direction == SND_COMPRESS_PLAYBACK) { if (codec_param->codec.id == SND_AUDIOCODEC_PCM) { trans->sink.codec_format = @@ -428,7 +529,7 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream, pr_debug("%s: ASM client allocated, callback %pK\n", __func__, loopback_event_handler); trans->session_id = trans->audio_client->session; - trans->audio_client->perf_mode = false; + trans->audio_client->perf_mode = pdata->perf_mode; ret = q6asm_open_transcode_loopback(trans->audio_client, bit_width, trans->source.codec_format, @@ -447,7 +548,7 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream, if (trans->source.codec_format != FORMAT_LINEAR_PCM) msm_pcm_routing_reg_phy_compr_stream( soc_pcm_tx->dai_link->be_id, - trans->audio_client->perf_mode, + false, trans->session_id, SNDRV_PCM_STREAM_CAPTURE, COMPRESSED_PASSTHROUGH_GEN); @@ -460,7 +561,7 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream, /* Opening Rx ADM in LOW_LATENCY mode by default */ msm_pcm_routing_reg_phy_stream( soc_pcm_rx->dai_link->be_id, - true, + trans->audio_client->perf_mode, trans->session_id, SNDRV_PCM_STREAM_PLAYBACK); pr_debug("%s: Successfully opened ADM sessions\n", __func__); @@ -493,6 +594,46 @@ static int msm_transcode_loopback_get_caps(struct snd_compr_stream *cstream, return 0; } +static int msm_transcode_loopback_set_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + struct snd_soc_pcm_runtime *rtd; + struct trans_loopback_pdata *pdata; + + if (!metadata || !cstream) { + pr_err("%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + rtd = snd_pcm_substream_chip(cstream); + pdata = snd_soc_platform_get_drvdata(rtd->platform); + + switch (metadata->key) { + case SNDRV_COMPRESS_LATENCY_MODE: + { + switch (metadata->value[0]) { + case SNDRV_COMPRESS_LEGACY_LATENCY_MODE: + pdata->perf_mode = LEGACY_PCM_MODE; + break; + case SNDRV_COMPRESS_LOW_LATENCY_MODE: + pdata->perf_mode = LOW_LATENCY_PCM_MODE; + break; + default: + pr_debug("%s: Unsupported latency mode %d, default to Legacy\n", + __func__, metadata->value[0]); + pdata->perf_mode = LEGACY_PCM_MODE; + break; + } + } + break; + default: + pr_debug("%s: Unsupported metadata %d\n", + __func__, metadata->key); + break; + } + return 0; +} + static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -557,7 +698,7 @@ done: return ret; } -static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol, +static int msm_transcode_shm_ion_fd_map_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); @@ -566,7 +707,6 @@ static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol, snd_soc_component_get_drvdata(comp); struct snd_compr_stream *cstream = NULL; struct msm_transcode_loopback *prtd; - int fd; int ret = 0; if (fe_id >= MSM_FRONTEND_DAI_MAX) { @@ -596,10 +736,36 @@ static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol, goto done; } - memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd)); - ret = q6asm_send_ion_fd(prtd->audio_client, fd); + memcpy(&prtd->shm_ion_fd, ucontrol->value.bytes.data, + sizeof(prtd->shm_ion_fd)); + ret = q6asm_audio_map_shm_fd(prtd->audio_client, + &prtd->shm_ion_client, + &prtd->shm_ion_handle, prtd->shm_ion_fd); if (ret < 0) - pr_err("%s: failed to register ion fd\n", __func__); + pr_err("%s: failed to map shm mem\n", __func__); +done: + return ret; +} + + +static int msm_transcode_lib_ion_fd_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + int ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + memcpy(&pdata->ion_fd[fe_id], ucontrol->value.bytes.data, + sizeof(pdata->ion_fd[fe_id])); done: return ret; } @@ -663,6 +829,141 @@ done: return ret; } +static int msm_transcode_playback_app_type_cfg_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[ + APP_TYPE_CONFIG_IDX_APP_TYPE]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[ + APP_TYPE_CONFIG_IDX_ACDB_ID]; + if (ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[ + APP_TYPE_CONFIG_IDX_SAMPLE_RATE]; + pr_debug("%s: fe_id %llu session_type %d be_id %d app_type %d acdb_dev_id %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_transcode_playback_stream_app_type_cfg set failed returned %d\n", + __func__, ret); + + return ret; +} + +static int msm_transcode_playback_app_type_cfg_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_transcode_playback_stream_app_type_cfg get failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_APP_TYPE] = + cfg_data.app_type; + ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_ACDB_ID] = + cfg_data.acdb_dev_id; + ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] = + cfg_data.sample_rate; + ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_transcode_set_volume(struct snd_compr_stream *cstream, + uint32_t master_gain) +{ + int rc = 0; + struct msm_transcode_loopback *prtd; + struct snd_soc_pcm_runtime *rtd; + + pr_debug("%s: master_gain %d\n", __func__, master_gain); + if (!cstream || !cstream->runtime) { + pr_err("%s: session not active\n", __func__); + return -EPERM; + } + rtd = cstream->private_data; + prtd = cstream->runtime->private_data; + + if (!rtd || !rtd->platform || !prtd || !prtd->audio_client) { + pr_err("%s: invalid rtd, prtd or audio client", __func__); + return -EINVAL; + } + + rc = q6asm_set_volume(prtd->audio_client, master_gain); + if (rc < 0) + pr_err("%s: Send vol gain command failed rc=%d\n", + __func__, rc); + + return rc; +} + +static int msm_transcode_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + uint32_t ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + + cstream = pdata->cstream[fe_id]; + pdata->master_gain = ucontrol->value.integer.value[0]; + + pr_debug("%s: fe_id %lu master_gain %d\n", + __func__, fe_id, pdata->master_gain); + if (cstream) + ret = msm_transcode_set_volume(cstream, pdata->master_gain); + return ret; +} + +static int msm_transcode_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: fe_id %lu\n", __func__, fe_id); + ucontrol->value.integer.value[0] = pdata->master_gain; + + return 0; +} + static int msm_transcode_stream_cmd_control( struct snd_soc_pcm_runtime *rtd) { @@ -773,7 +1074,8 @@ done: return ret; } -static int msm_transcode_add_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) +static int msm_transcode_add_shm_ion_fd_cmd_control( + struct snd_soc_pcm_runtime *rtd) { const char *mixer_ctl_name = "Playback ION FD"; const char *deviceNo = "NN"; @@ -785,7 +1087,53 @@ static int msm_transcode_add_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) .name = "?", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = msm_adsp_stream_cmd_info, - .put = msm_transcode_ion_fd_map_put, + .put = msm_transcode_shm_ion_fd_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_ion_fd_config_control[0].name = mixer_str; + fe_ion_fd_config_control[0].private_value = rtd->dai_link->be_id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_ion_fd_config_control, + ARRAY_SIZE(fe_ion_fd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_add_lib_ion_fd_cmd_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback ION LIB FD"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_ion_fd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_transcode_lib_ion_fd_map_put, .private_value = 0, } }; @@ -864,6 +1212,99 @@ done: return ret; } +static int msm_transcode_app_type_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 5; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_transcode_add_app_type_cfg_control( + struct snd_soc_pcm_runtime *rtd) +{ + char mixer_str[32]; + int rc = 0; + struct snd_kcontrol_new fe_app_type_cfg_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_transcode_app_type_cfg_info, + .put = msm_transcode_playback_app_type_cfg_put, + .get = msm_transcode_playback_app_type_cfg_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + + return -EINVAL; + } + + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) { + + snprintf(mixer_str, sizeof(mixer_str), + "Audio Stream %d App Type Cfg", + rtd->pcm->device); + + fe_app_type_cfg_control[0].name = mixer_str; + fe_app_type_cfg_control[0].private_value = rtd->dai_link->be_id; + + fe_app_type_cfg_control[0].put = + msm_transcode_playback_app_type_cfg_put; + fe_app_type_cfg_control[0].get = + msm_transcode_playback_app_type_cfg_get; + + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_app_type_cfg_control, + ARRAY_SIZE(fe_app_type_cfg_control)); + } + + return rc; +} +static int msm_transcode_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TRANSCODE_LR_VOL_MAX_STEPS; + return 0; +} + +static int msm_transcode_add_volume_control(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol_new fe_volume_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Transcode Loopback Rx Volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_transcode_volume_info, + .get = msm_transcode_volume_get, + .put = msm_transcode_volume_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return -EINVAL; + } + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) { + fe_volume_control[0].private_value = rtd->dai_link->be_id; + pr_debug("Registering new mixer ctl %s", + fe_volume_control[0].name); + snd_soc_add_platform_controls(rtd->platform, fe_volume_control, + ARRAY_SIZE(fe_volume_control)); + } + return 0; +} + static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd) { int rc; @@ -877,9 +1318,14 @@ static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd) pr_err("%s: ADSP Stream callback Control open failed\n", __func__); - rc = msm_transcode_add_ion_fd_cmd_control(rtd); + rc = msm_transcode_add_shm_ion_fd_cmd_control(rtd); if (rc) - pr_err("%s: Could not add transcode ion fd Control\n", + pr_err("%s: Could not add transcode shm ion fd Control\n", + __func__); + + rc = msm_transcode_add_lib_ion_fd_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add transcode lib ion fd Control\n", __func__); rc = msm_transcode_add_event_ack_cmd_control(rtd); @@ -887,6 +1333,16 @@ static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd) pr_err("%s: Could not add transcode event ack Control\n", __func__); + rc = msm_transcode_add_app_type_cfg_control(rtd); + if (rc) + pr_err("%s: Could not add Compr App Type Cfg Control\n", + __func__); + + rc = msm_transcode_add_volume_control(rtd); + if (rc) + pr_err("%s: Could not add transcode volume Control\n", + __func__); + return 0; } @@ -896,6 +1352,7 @@ static struct snd_compr_ops msm_transcode_loopback_ops = { .trigger = msm_transcode_loopback_trigger, .set_params = msm_transcode_loopback_set_params, .get_caps = msm_transcode_loopback_get_caps, + .set_metadata = msm_transcode_loopback_set_metadata, }; @@ -910,6 +1367,7 @@ static int msm_transcode_loopback_probe(struct snd_soc_platform *platform) if (!pdata) return -ENOMEM; + pdata->perf_mode = LOW_LATENCY_PCM_MODE; snd_soc_platform_set_drvdata(platform, pdata); return 0; } diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index e1bfc950d0e3..018681309f2e 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -2364,7 +2364,8 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, struct adm_cmd_device_open_v5 open; struct adm_cmd_device_open_v6 open_v6; int ret = 0; - int port_idx, copp_idx, flags; + int port_idx, flags; + int copp_idx = -1; int tmp_port = q6audio_get_port_id(port_id); pr_debug("%s:port %#x path:%d rate:%d mode:%d perf_mode:%d,topo_id %d\n", @@ -2418,8 +2419,17 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY)) rate = 16000; - copp_idx = adm_get_idx_if_copp_exists(port_idx, topology, perf_mode, - rate, bit_width, app_type); + /* + * Routing driver reuses the same adm for streams with the same + * app_type, sample_rate etc. + * This isn't allowed for ULL streams as per the DSP interface + */ + if (perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) + copp_idx = adm_get_idx_if_copp_exists(port_idx, topology, + perf_mode, + rate, bit_width, + app_type); + if (copp_idx < 0) { copp_idx = adm_get_next_available_copp(port_idx); if (copp_idx >= MAX_COPPS_PER_PORT) { diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index d3cff9fccf59..14f9411104b3 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -36,6 +36,7 @@ #include <sound/apr_audio-v2.h> #include <sound/q6asm-v2.h> +#include <sound/q6core.h> #include <sound/q6audio-v2.h> #include <sound/audio_cal_utils.h> #include <sound/adsp_err.h> @@ -423,14 +424,22 @@ static void config_debug_fs_run(void) } } -static void config_debug_fs_write(struct audio_buffer *ab) +static void config_debug_fs_write(struct audio_buffer *ab, int offset) { if (out_enable_flag) { char zero_pattern[2] = {0x00, 0x00}; + char *data; + + if ((offset < 0) || (offset > ab->size)) { + pr_err("Invalid offset %d", offset); + return; + } + + data = (char *)ab->data + offset; /* If First two byte is non zero and last two byte is zero then it is warm output pattern */ - if ((strncmp(((char *)ab->data), zero_pattern, 2)) && - (!strncmp(((char *)ab->data + 2), zero_pattern, 2))) { + if ((strncmp(data, zero_pattern, 2)) && + (!strncmp((data + 2), zero_pattern, 2))) { do_gettimeofday(&out_warm_tv); pr_debug("%s: WARM:apr_send_pkt at %ld sec %ld microsec\n", __func__, @@ -440,8 +449,8 @@ static void config_debug_fs_write(struct audio_buffer *ab) } /* If First two byte is zero and last two byte is non zero then it is cont ouput pattern */ - else if ((!strncmp(((char *)ab->data), zero_pattern, 2)) - && (strncmp(((char *)ab->data + 2), zero_pattern, 2))) { + else if ((!strncmp(data, zero_pattern, 2)) + && (strncmp((data + 2), zero_pattern, 2))) { do_gettimeofday(&out_cont_tv); pr_debug("%s: CONT:apr_send_pkt at %ld sec %ld microsec\n", __func__, @@ -488,7 +497,7 @@ outbuf_fail: return; } #else -static void config_debug_fs_write(struct audio_buffer *ab) +static void config_debug_fs_write(struct audio_buffer *ab, int offset) { return; } @@ -7109,13 +7118,11 @@ fail_cmd: return rc; } -int q6asm_send_ion_fd(struct audio_client *ac, int fd) +int q6asm_audio_map_shm_fd(struct audio_client *ac, struct ion_client **client, + struct ion_handle **handle, int fd) { - struct ion_client *client; - struct ion_handle *handle; ion_phys_addr_t paddr; size_t pa_len = 0; - void *vaddr; int ret; int sz = 0; struct avs_rtic_shared_mem_addr shm; @@ -7131,19 +7138,11 @@ int q6asm_send_ion_fd(struct audio_client *ac, int fd) goto fail_cmd; } - ret = msm_audio_ion_import("audio_mem_client", - &client, - &handle, - fd, - NULL, - 0, - &paddr, - &pa_len, - &vaddr); + ret = msm_audio_ion_phys_assign("audio_shm_mem_client", client, + handle, fd, + &paddr, &pa_len, HLOS_TO_ADSP); if (ret) { - pr_err("%s: audio ION import failed, rc = %d\n", - __func__, ret); - ret = -ENOMEM; + pr_err("%s: shm ION phys failed, rc = %d\n", __func__, ret); goto fail_cmd; } /* get payload length */ @@ -7546,7 +7545,9 @@ int q6asm_set_mfc_panning_params(struct audio_client *ac, struct asm_stream_param_data_v2 data; struct audproc_chmixer_param_coeff pan_cfg; uint16_t variable_payload = 0; - uint16_t *asm_params = NULL; + char *asm_params = NULL; + uint16_t ii; + uint16_t *dst_gain_ptr = NULL; sz = rc = i = 0; if (ac == NULL) { @@ -7607,7 +7608,7 @@ int q6asm_set_mfc_panning_params(struct audio_client *ac, variable_payload = pan_param->num_output_channels * sizeof(uint16_t)+ pan_param->num_input_channels * sizeof(uint16_t) + - pan_param->num_output_channels * sizeof(uint16_t) * + pan_param->num_output_channels * pan_param->num_input_channels * sizeof(uint16_t); i = (variable_payload % sizeof(uint32_t)); variable_payload += (i == 0) ? 0 : sizeof(uint32_t) - i; @@ -7667,15 +7668,16 @@ int q6asm_set_mfc_panning_params(struct audio_client *ac, pan_param->num_output_channels * sizeof(uint16_t)), pan_param->input_channel_map, pan_param->num_input_channels * sizeof(uint16_t)); - memcpy(((u8 *)asm_params + sizeof(struct apr_hdr) + + + dst_gain_ptr = (uint16_t *) ((u8 *)asm_params + sizeof(struct apr_hdr) + sizeof(struct asm_stream_cmd_set_pp_params_v2) + sizeof(struct asm_stream_param_data_v2) + sizeof(struct audproc_chmixer_param_coeff) + (pan_param->num_output_channels * sizeof(uint16_t)) + - (pan_param->num_input_channels * sizeof(uint16_t))), - pan_param->gain, - (pan_param->num_output_channels * sizeof(uint16_t)) * (pan_param->num_input_channels * sizeof(uint16_t))); + for (ii = 0; ii < pan_param->num_output_channels * + pan_param->num_input_channels; ii++) + dst_gain_ptr[ii] = (uint16_t) pan_param->gain[ii]; rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params); if (rc < 0) { @@ -7979,6 +7981,7 @@ int q6asm_async_write(struct audio_client *ac, u32 liomode; u32 io_compressed; u32 io_compressed_stream; + int offset = 0; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); @@ -8040,7 +8043,10 @@ int q6asm_async_write(struct audio_client *ac, } } - config_debug_fs_write(ab); + if (ab != NULL) { + offset = lbuf_phys_addr - ab->phys; + config_debug_fs_write(ab, offset); + } rc = apr_send_pkt(ac->apr, (uint32_t *) &write); if (rc < 0) { @@ -8187,7 +8193,7 @@ int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts, write.mem_map_handle); mutex_unlock(&port->lock); - config_debug_fs_write(ab); + config_debug_fs_write(ab, 0); rc = apr_send_pkt(ac->apr, (uint32_t *) &write); if (rc < 0) { diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c index 4340d31c218c..6fed443186e5 100644 --- a/sound/soc/msm/qdsp6v2/q6core.c +++ b/sound/soc/msm/qdsp6v2/q6core.c @@ -119,6 +119,18 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) q6core_lcl.bus_bw_resp_received = 1; wake_up(&q6core_lcl.bus_bw_req_wait); break; + case AVCS_CMD_ADD_POOL_PAGES: + pr_debug("%s: Cmd = AVCS_CMD_ADD_POOL_PAGES status[0x%x]\n", + __func__, payload1[1]); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMD_REMOVE_POOL_PAGES: + pr_debug("%s: Cmd = AVCS_CMD_REMOVE_POOL_PAGES status[0x%x]\n", + __func__, payload1[1]); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; default: pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n", __func__, @@ -542,6 +554,56 @@ done: return ret; } +int q6core_add_remove_pool_pages(ion_phys_addr_t buf_add, uint32_t bufsz, + uint32_t mempool_id, bool add_pages) +{ + struct avs_mem_assign_region mem_pool; + int ret = 0, sz; + + if (add_pages) + mem_pool.hdr.opcode = AVCS_CMD_ADD_POOL_PAGES; + else + mem_pool.hdr.opcode = AVCS_CMD_REMOVE_POOL_PAGES; + + /* get payload length */ + sz = sizeof(struct avs_mem_assign_region); + mem_pool.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + mem_pool.hdr.src_port = 0; + mem_pool.hdr.dest_port = 0; + mem_pool.hdr.token = 0; + mem_pool.hdr.pkt_size = sz; + mem_pool.pool_id = mempool_id; + mem_pool.size = bufsz; + mem_pool.addr_lsw = lower_32_bits(buf_add); + mem_pool.addr_msw = msm_audio_populate_upper_32_bits(buf_add); + pr_debug("%s: sending memory map, size %d\n", + __func__, bufsz); + + q6core_lcl.bus_bw_resp_received = 0; + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&mem_pool); + if (ret < 0) { + pr_err("%s: library map region failed %d\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for library memory map\n", + __func__); + ret = -ETIME; + goto done; + } + ret = 0; +done: + return ret; +} + static int q6core_dereg_all_custom_topologies(void) { int ret = 0; diff --git a/sound/soc/msm/sdm660-ext-dai-links.c b/sound/soc/msm/sdm660-ext-dai-links.c index 1c03d8c9e797..30c3ffe2347d 100644 --- a/sound/soc/msm/sdm660-ext-dai-links.c +++ b/sound/soc/msm/sdm660-ext-dai-links.c @@ -1270,10 +1270,10 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, {/* hw:x,33 */ - .name = MSM_DAILINK_NAME(Compress9), - .stream_name = "Compress9", + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp-noirq", .dynamic = 1, .dpcm_capture = 1, .dpcm_playback = 1, diff --git a/sound/soc/msm/sdm660-internal.c b/sound/soc/msm/sdm660-internal.c index f4219148e81c..948fb287023d 100644 --- a/sound/soc/msm/sdm660-internal.c +++ b/sound/soc/msm/sdm660-internal.c @@ -1291,6 +1291,9 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_sync(dapm); + + dapm = snd_soc_codec_get_dapm(dig_cdc); snd_soc_dapm_ignore_suspend(dapm, "DMIC1"); snd_soc_dapm_ignore_suspend(dapm, "DMIC2"); snd_soc_dapm_ignore_suspend(dapm, "DMIC3"); @@ -2188,10 +2191,10 @@ static struct snd_soc_dai_link msm_int_dai[] = { .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, {/* hw:x,33 */ - .name = MSM_DAILINK_NAME(Compress9), - .stream_name = "Compress9", + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp-noirq", .dynamic = 1, .dpcm_capture = 1, .dpcm_playback = 1, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e63b4346e82e..fae57d18bbd7 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3063,7 +3063,8 @@ static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm, */ void snd_soc_card_change_online_state(struct snd_soc_card *soc_card, int online) { - snd_card_change_online_state(soc_card->snd_card, online); + if (soc_card && soc_card->snd_card) + snd_card_change_online_state(soc_card->snd_card, online); } EXPORT_SYMBOL(snd_soc_card_change_online_state); diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 2bba7dbc35f9..61f64dcd5374 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -384,6 +384,9 @@ static void snd_complete_urb(struct urb *urb) if (unlikely(atomic_read(&ep->chip->shutdown))) goto exit_clear; + if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags))) + goto exit_clear; + if (usb_pipeout(ep->pipe)) { retire_outbound_urb(ep, ctx); /* can be stopped during retire callback */ diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h index fa7208a32d76..8a679b21f0c4 100644 --- a/tools/include/linux/compiler.h +++ b/tools/include/linux/compiler.h @@ -115,4 +115,13 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s #define WRITE_ONCE(x, val) \ ({ union { typeof(x) __val; char __c[1]; } __u = { .__val = (val) }; __write_once_size(&(x), __u.__c, sizeof(x)); __u.__val; }) + +#ifndef __fallthrough +# if defined(__GNUC__) && __GNUC__ >= 7 +# define __fallthrough __attribute__ ((fallthrough)) +# else +# define __fallthrough +# endif +#endif + #endif /* _TOOLS_LINUX_COMPILER_H */ diff --git a/tools/lib/lockdep/uinclude/linux/lockdep.h b/tools/lib/lockdep/uinclude/linux/lockdep.h index c808c7d02d21..e69118b2077e 100644 --- a/tools/lib/lockdep/uinclude/linux/lockdep.h +++ b/tools/lib/lockdep/uinclude/linux/lockdep.h @@ -8,7 +8,7 @@ #include <linux/utsname.h> #include <linux/compiler.h> -#define MAX_LOCK_DEPTH 2000UL +#define MAX_LOCK_DEPTH 255UL #define asmlinkage #define __visible diff --git a/tools/lib/traceevent/plugin_sched_switch.c b/tools/lib/traceevent/plugin_sched_switch.c index f1ce60065258..ec30c2fcbac0 100644 --- a/tools/lib/traceevent/plugin_sched_switch.c +++ b/tools/lib/traceevent/plugin_sched_switch.c @@ -111,7 +111,7 @@ static int sched_switch_handler(struct trace_seq *s, trace_seq_printf(s, "%lld ", val); if (pevent_get_field_val(s, event, "prev_prio", record, &val, 0) == 0) - trace_seq_printf(s, "[%lld] ", val); + trace_seq_printf(s, "[%d] ", (int) val); if (pevent_get_field_val(s, event, "prev_state", record, &val, 0) == 0) write_state(s, val); @@ -129,7 +129,7 @@ static int sched_switch_handler(struct trace_seq *s, trace_seq_printf(s, "%lld", val); if (pevent_get_field_val(s, event, "next_prio", record, &val, 0) == 0) - trace_seq_printf(s, " [%lld]", val); + trace_seq_printf(s, " [%d]", (int) val); return 0; } diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 74c265e0ffa0..fb1c9ddc3478 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -566,9 +566,9 @@ install-tests: all install-gtk $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \ $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' -install-bin: install-tools install-tests +install-bin: install-tools install-tests install-traceevent-plugins -install: install-bin try-install-man install-traceevent-plugins +install: install-bin try-install-man install-python_ext: $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c index d28c1b6a3b54..fa5d17af88b7 100644 --- a/tools/perf/arch/x86/tests/intel-cqm.c +++ b/tools/perf/arch/x86/tests/intel-cqm.c @@ -17,7 +17,7 @@ static pid_t spawn(void) if (pid) return pid; - while(1); + while(1) sleep(5); return 0; } diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c index 9223c164e545..1f86ee8fb831 100644 --- a/tools/perf/arch/x86/util/dwarf-regs.c +++ b/tools/perf/arch/x86/util/dwarf-regs.c @@ -63,6 +63,8 @@ struct pt_regs_offset { # define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)} #endif +/* TODO: switching by dwarf address size */ +#ifndef __x86_64__ static const struct pt_regs_offset x86_32_regoffset_table[] = { REG_OFFSET_NAME_32("%ax", eax), REG_OFFSET_NAME_32("%cx", ecx), @@ -75,6 +77,8 @@ static const struct pt_regs_offset x86_32_regoffset_table[] = { REG_OFFSET_END, }; +#define regoffset_table x86_32_regoffset_table +#else static const struct pt_regs_offset x86_64_regoffset_table[] = { REG_OFFSET_NAME_64("%ax", rax), REG_OFFSET_NAME_64("%dx", rdx), @@ -95,11 +99,7 @@ static const struct pt_regs_offset x86_64_regoffset_table[] = { REG_OFFSET_END, }; -/* TODO: switching by dwarf address size */ -#ifdef __x86_64__ #define regoffset_table x86_64_regoffset_table -#else -#define regoffset_table x86_32_regoffset_table #endif /* Minus 1 for the ending REG_OFFSET_END */ diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 492df2752a2d..b4eb5b679081 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -1570,13 +1570,13 @@ static int __bench_numa(const char *name) "GB/sec,", "total-speed", "GB/sec total speed"); if (g->p.show_details >= 2) { - char tname[32]; + char tname[14 + 2 * 10 + 1]; struct thread_data *td; for (p = 0; p < g->p.nr_proc; p++) { for (t = 0; t < g->p.nr_threads; t++) { - memset(tname, 0, 32); + memset(tname, 0, sizeof(tname)); td = g->threads + p*g->p.nr_threads + t; - snprintf(tname, 32, "process%d:thread%d", p, t); + snprintf(tname, sizeof(tname), "process%d:thread%d", p, t); print_res(tname, td->speed_gbs, "GB/sec", "thread-speed", "GB/sec/thread speed"); print_res(tname, td->system_time_ns / 1e9, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 368d1e1561f7..48840556bf2d 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1253,21 +1253,19 @@ static int is_directory(const char *base_path, const struct dirent *dent) return S_ISDIR(st.st_mode); } -#define for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next)\ - while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \ - lang_next) \ - if ((lang_dirent.d_type == DT_DIR || \ - (lang_dirent.d_type == DT_UNKNOWN && \ - is_directory(scripts_path, &lang_dirent))) && \ - (strcmp(lang_dirent.d_name, ".")) && \ - (strcmp(lang_dirent.d_name, ".."))) - -#define for_each_script(lang_path, lang_dir, script_dirent, script_next)\ - while (!readdir_r(lang_dir, &script_dirent, &script_next) && \ - script_next) \ - if (script_dirent.d_type != DT_DIR && \ - (script_dirent.d_type != DT_UNKNOWN || \ - !is_directory(lang_path, &script_dirent))) +#define for_each_lang(scripts_path, scripts_dir, lang_dirent) \ + while ((lang_dirent = readdir(scripts_dir)) != NULL) \ + if ((lang_dirent->d_type == DT_DIR || \ + (lang_dirent->d_type == DT_UNKNOWN && \ + is_directory(scripts_path, lang_dirent))) && \ + (strcmp(lang_dirent->d_name, ".")) && \ + (strcmp(lang_dirent->d_name, ".."))) + +#define for_each_script(lang_path, lang_dir, script_dirent) \ + while ((script_dirent = readdir(lang_dir)) != NULL) \ + if (script_dirent->d_type != DT_DIR && \ + (script_dirent->d_type != DT_UNKNOWN || \ + !is_directory(lang_path, script_dirent))) #define RECORD_SUFFIX "-record" @@ -1413,7 +1411,7 @@ static int list_available_scripts(const struct option *opt __maybe_unused, const char *s __maybe_unused, int unset __maybe_unused) { - struct dirent *script_next, *lang_next, script_dirent, lang_dirent; + struct dirent *script_dirent, *lang_dirent; char scripts_path[MAXPATHLEN]; DIR *scripts_dir, *lang_dir; char script_path[MAXPATHLEN]; @@ -1428,19 +1426,19 @@ static int list_available_scripts(const struct option *opt __maybe_unused, if (!scripts_dir) return -1; - for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { + for_each_lang(scripts_path, scripts_dir, lang_dirent) { snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, - lang_dirent.d_name); + lang_dirent->d_name); lang_dir = opendir(lang_path); if (!lang_dir) continue; - for_each_script(lang_path, lang_dir, script_dirent, script_next) { - script_root = get_script_root(&script_dirent, REPORT_SUFFIX); + for_each_script(lang_path, lang_dir, script_dirent) { + script_root = get_script_root(script_dirent, REPORT_SUFFIX); if (script_root) { desc = script_desc__findnew(script_root); snprintf(script_path, MAXPATHLEN, "%s/%s", - lang_path, script_dirent.d_name); + lang_path, script_dirent->d_name); read_script_info(desc, script_path); free(script_root); } @@ -1528,7 +1526,7 @@ static int check_ev_match(char *dir_name, char *scriptname, */ int find_scripts(char **scripts_array, char **scripts_path_array) { - struct dirent *script_next, *lang_next, script_dirent, lang_dirent; + struct dirent *script_dirent, *lang_dirent; char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN]; DIR *scripts_dir, *lang_dir; struct perf_session *session; @@ -1551,9 +1549,9 @@ int find_scripts(char **scripts_array, char **scripts_path_array) return -1; } - for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { + for_each_lang(scripts_path, scripts_dir, lang_dirent) { snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path, - lang_dirent.d_name); + lang_dirent->d_name); #ifdef NO_LIBPERL if (strstr(lang_path, "perl")) continue; @@ -1567,16 +1565,16 @@ int find_scripts(char **scripts_array, char **scripts_path_array) if (!lang_dir) continue; - for_each_script(lang_path, lang_dir, script_dirent, script_next) { + for_each_script(lang_path, lang_dir, script_dirent) { /* Skip those real time scripts: xxxtop.p[yl] */ - if (strstr(script_dirent.d_name, "top.")) + if (strstr(script_dirent->d_name, "top.")) continue; sprintf(scripts_path_array[i], "%s/%s", lang_path, - script_dirent.d_name); - temp = strchr(script_dirent.d_name, '.'); + script_dirent->d_name); + temp = strchr(script_dirent->d_name, '.'); snprintf(scripts_array[i], - (temp - script_dirent.d_name) + 1, - "%s", script_dirent.d_name); + (temp - script_dirent->d_name) + 1, + "%s", script_dirent->d_name); if (check_ev_match(lang_path, scripts_array[i], session)) @@ -1594,7 +1592,7 @@ int find_scripts(char **scripts_array, char **scripts_path_array) static char *get_script_path(const char *script_root, const char *suffix) { - struct dirent *script_next, *lang_next, script_dirent, lang_dirent; + struct dirent *script_dirent, *lang_dirent; char scripts_path[MAXPATHLEN]; char script_path[MAXPATHLEN]; DIR *scripts_dir, *lang_dir; @@ -1607,21 +1605,21 @@ static char *get_script_path(const char *script_root, const char *suffix) if (!scripts_dir) return NULL; - for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { + for_each_lang(scripts_path, scripts_dir, lang_dirent) { snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, - lang_dirent.d_name); + lang_dirent->d_name); lang_dir = opendir(lang_path); if (!lang_dir) continue; - for_each_script(lang_path, lang_dir, script_dirent, script_next) { - __script_root = get_script_root(&script_dirent, suffix); + for_each_script(lang_path, lang_dir, script_dirent) { + __script_root = get_script_root(script_dirent, suffix); if (__script_root && !strcmp(script_root, __script_root)) { free(__script_root); closedir(lang_dir); closedir(scripts_dir); snprintf(script_path, MAXPATHLEN, "%s/%s", - lang_path, script_dirent.d_name); + lang_path, script_dirent->d_name); return strdup(script_path); } free(__script_root); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 7e2e72e6d9d1..4a8a02c302d2 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -636,7 +636,7 @@ repeat: case -1: if (errno == EINTR) continue; - /* Fall trhu */ + __fallthrough; default: c = getc(stdin); tcsetattr(0, TCSAFLUSH, &save); diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index c783d8fd3a80..ebe7115c751a 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1617,6 +1617,7 @@ static int trace__process_event(struct trace *trace, struct machine *machine, color_fprintf(trace->output, PERF_COLOR_RED, "LOST %" PRIu64 " events!\n", event->lost.lost); ret = machine__process_lost_event(machine, event, sample); + break; default: ret = machine__process_event(machine, event, sample); break; diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 636d7b42d844..54af2f2e2ee4 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1727,15 +1727,14 @@ static int test_pmu_events(void) } while (!ret && (ent = readdir(dir))) { -#define MAX_NAME 100 struct evlist_test e; - char name[MAX_NAME]; + char name[2 * NAME_MAX + 1 + 12 + 3]; if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) continue; - snprintf(name, MAX_NAME, "cpu/event=%s/u", ent->d_name); + snprintf(name, sizeof(name), "cpu/event=%s/u", ent->d_name); e.name = name; e.check = test__checkevent_pmu_events; @@ -1743,11 +1742,10 @@ static int test_pmu_events(void) ret = test_event(&e); if (ret) break; - snprintf(name, MAX_NAME, "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name); + snprintf(name, sizeof(name), "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name); e.name = name; e.check = test__checkevent_pmu_events_mix; ret = test_event(&e); -#undef MAX_NAME } closedir(dir); diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index e9703c0829f1..07b5f5951b25 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -702,7 +702,7 @@ static void __ui_browser__line_arrow_down(struct ui_browser *browser, ui_browser__gotorc(browser, row, column + 1); SLsmg_draw_hline(2); - if (row++ == 0) + if (++row == 0) goto out; } else row = 0; diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index d4d7cc27252f..718bd46d47fa 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -755,11 +755,11 @@ static int annotate_browser__run(struct annotate_browser *browser, nd = browser->curr_hot; break; case K_UNTAB: - if (nd != NULL) + if (nd != NULL) { nd = rb_next(nd); if (nd == NULL) nd = rb_first(&browser->entries); - else + } else nd = browser->curr_hot; break; case K_F1: diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 956187bf1a85..26cba64345e3 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -416,7 +416,7 @@ static int __event__synthesize_thread(union perf_event *comm_event, { char filename[PATH_MAX]; DIR *tasks; - struct dirent dirent, *next; + struct dirent *dirent; pid_t tgid, ppid; int rc = 0; @@ -445,11 +445,11 @@ static int __event__synthesize_thread(union perf_event *comm_event, return 0; } - while (!readdir_r(tasks, &dirent, &next) && next) { + while ((dirent = readdir(tasks)) != NULL) { char *end; pid_t _pid; - _pid = strtol(dirent.d_name, &end, 10); + _pid = strtol(dirent->d_name, &end, 10); if (*end) continue; @@ -558,7 +558,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, { DIR *proc; char proc_path[PATH_MAX]; - struct dirent dirent, *next; + struct dirent *dirent; union perf_event *comm_event, *mmap_event, *fork_event; int err = -1; @@ -583,9 +583,9 @@ int perf_event__synthesize_threads(struct perf_tool *tool, if (proc == NULL) goto out_free_fork; - while (!readdir_r(proc, &dirent, &next) && next) { + while ((dirent = readdir(proc)) != NULL) { char *end; - pid_t pid = strtol(dirent.d_name, &end, 10); + pid_t pid = strtol(dirent->d_name, &end, 10); if (*end) /* only interested in proper numerical dirents */ continue; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 71df7acf8643..eeeae0629ad3 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c @@ -22,6 +22,7 @@ #include <errno.h> #include <stdint.h> #include <inttypes.h> +#include <linux/compiler.h> #include "../cache.h" #include "../util.h" @@ -63,6 +64,25 @@ enum intel_pt_pkt_state { INTEL_PT_STATE_FUP_NO_TIP, }; +static inline bool intel_pt_sample_time(enum intel_pt_pkt_state pkt_state) +{ + switch (pkt_state) { + case INTEL_PT_STATE_NO_PSB: + case INTEL_PT_STATE_NO_IP: + case INTEL_PT_STATE_ERR_RESYNC: + case INTEL_PT_STATE_IN_SYNC: + case INTEL_PT_STATE_TNT: + return true; + case INTEL_PT_STATE_TIP: + case INTEL_PT_STATE_TIP_PGD: + case INTEL_PT_STATE_FUP: + case INTEL_PT_STATE_FUP_NO_TIP: + return false; + default: + return true; + }; +} + #ifdef INTEL_PT_STRICT #define INTEL_PT_STATE_ERR1 INTEL_PT_STATE_NO_PSB #define INTEL_PT_STATE_ERR2 INTEL_PT_STATE_NO_PSB @@ -90,6 +110,7 @@ struct intel_pt_decoder { bool have_tma; bool have_cyc; bool fixup_last_mtc; + bool have_last_ip; uint64_t pos; uint64_t last_ip; uint64_t ip; @@ -97,6 +118,7 @@ struct intel_pt_decoder { uint64_t timestamp; uint64_t tsc_timestamp; uint64_t ref_timestamp; + uint64_t sample_timestamp; uint64_t ret_addr; uint64_t ctc_timestamp; uint64_t ctc_delta; @@ -124,8 +146,6 @@ struct intel_pt_decoder { bool have_calc_cyc_to_tsc; int exec_mode; unsigned int insn_bytes; - uint64_t sign_bit; - uint64_t sign_bits; uint64_t period; enum intel_pt_period_type period_type; uint64_t tot_insn_cnt; @@ -139,6 +159,7 @@ struct intel_pt_decoder { unsigned int fup_tx_flags; unsigned int tx_flags; uint64_t timestamp_insn_cnt; + uint64_t sample_insn_cnt; uint64_t stuck_ip; int no_progress; int stuck_ip_prd; @@ -192,9 +213,6 @@ struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params) decoder->data = params->data; decoder->return_compression = params->return_compression; - decoder->sign_bit = (uint64_t)1 << 47; - decoder->sign_bits = ~(((uint64_t)1 << 48) - 1); - decoder->period = params->period; decoder->period_type = params->period_type; @@ -363,21 +381,30 @@ int intel_pt__strerror(int code, char *buf, size_t buflen) return 0; } -static uint64_t intel_pt_calc_ip(struct intel_pt_decoder *decoder, - const struct intel_pt_pkt *packet, +static uint64_t intel_pt_calc_ip(const struct intel_pt_pkt *packet, uint64_t last_ip) { uint64_t ip; switch (packet->count) { - case 2: + case 1: ip = (last_ip & (uint64_t)0xffffffffffff0000ULL) | packet->payload; break; - case 4: + case 2: ip = (last_ip & (uint64_t)0xffffffff00000000ULL) | packet->payload; break; + case 3: + ip = packet->payload; + /* Sign-extend 6-byte ip */ + if (ip & (uint64_t)0x800000000000ULL) + ip |= (uint64_t)0xffff000000000000ULL; + break; + case 4: + ip = (last_ip & (uint64_t)0xffff000000000000ULL) | + packet->payload; + break; case 6: ip = packet->payload; break; @@ -385,16 +412,13 @@ static uint64_t intel_pt_calc_ip(struct intel_pt_decoder *decoder, return 0; } - if (ip & decoder->sign_bit) - return ip | decoder->sign_bits; - return ip; } static inline void intel_pt_set_last_ip(struct intel_pt_decoder *decoder) { - decoder->last_ip = intel_pt_calc_ip(decoder, &decoder->packet, - decoder->last_ip); + decoder->last_ip = intel_pt_calc_ip(&decoder->packet, decoder->last_ip); + decoder->have_last_ip = true; } static inline void intel_pt_set_ip(struct intel_pt_decoder *decoder) @@ -895,6 +919,7 @@ static int intel_pt_walk_insn(struct intel_pt_decoder *decoder, decoder->tot_insn_cnt += insn_cnt; decoder->timestamp_insn_cnt += insn_cnt; + decoder->sample_insn_cnt += insn_cnt; decoder->period_insn_cnt += insn_cnt; if (err) { @@ -1413,7 +1438,8 @@ static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder) case INTEL_PT_FUP: decoder->pge = true; - intel_pt_set_last_ip(decoder); + if (decoder->packet.count) + intel_pt_set_last_ip(decoder); break; case INTEL_PT_MODE_TSX: @@ -1617,6 +1643,8 @@ next: break; case INTEL_PT_PSB: + decoder->last_ip = 0; + decoder->have_last_ip = true; intel_pt_clear_stack(&decoder->stack); err = intel_pt_walk_psbend(decoder); if (err == -EAGAIN) @@ -1695,6 +1723,13 @@ next: } } +static inline bool intel_pt_have_ip(struct intel_pt_decoder *decoder) +{ + return decoder->packet.count && + (decoder->have_last_ip || decoder->packet.count == 3 || + decoder->packet.count == 6); +} + /* Walk PSB+ packets to get in sync. */ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder) { @@ -1708,6 +1743,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder) switch (decoder->packet.type) { case INTEL_PT_TIP_PGD: decoder->continuous_period = false; + __fallthrough; case INTEL_PT_TIP_PGE: case INTEL_PT_TIP: intel_pt_log("ERROR: Unexpected packet\n"); @@ -1715,8 +1751,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder) case INTEL_PT_FUP: decoder->pge = true; - if (decoder->last_ip || decoder->packet.count == 6 || - decoder->packet.count == 0) { + if (intel_pt_have_ip(decoder)) { uint64_t current_ip = decoder->ip; intel_pt_set_ip(decoder); @@ -1762,6 +1797,8 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder) decoder->pge = false; decoder->continuous_period = false; intel_pt_clear_tx_flags(decoder); + __fallthrough; + case INTEL_PT_TNT: decoder->have_tma = false; intel_pt_log("ERROR: Unexpected packet\n"); @@ -1802,27 +1839,21 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder) switch (decoder->packet.type) { case INTEL_PT_TIP_PGD: decoder->continuous_period = false; + __fallthrough; case INTEL_PT_TIP_PGE: case INTEL_PT_TIP: decoder->pge = decoder->packet.type != INTEL_PT_TIP_PGD; - if (decoder->last_ip || decoder->packet.count == 6 || - decoder->packet.count == 0) + if (intel_pt_have_ip(decoder)) intel_pt_set_ip(decoder); if (decoder->ip) return 0; break; case INTEL_PT_FUP: - if (decoder->overflow) { - if (decoder->last_ip || - decoder->packet.count == 6 || - decoder->packet.count == 0) - intel_pt_set_ip(decoder); - if (decoder->ip) - return 0; - } - if (decoder->packet.count) - intel_pt_set_last_ip(decoder); + if (intel_pt_have_ip(decoder)) + intel_pt_set_ip(decoder); + if (decoder->ip) + return 0; break; case INTEL_PT_MTC: @@ -1871,6 +1902,9 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder) break; case INTEL_PT_PSB: + decoder->last_ip = 0; + decoder->have_last_ip = true; + intel_pt_clear_stack(&decoder->stack); err = intel_pt_walk_psb(decoder); if (err) return err; @@ -1896,6 +1930,8 @@ static int intel_pt_sync_ip(struct intel_pt_decoder *decoder) { int err; + decoder->set_fup_tx_flags = false; + intel_pt_log("Scanning for full IP\n"); err = intel_pt_walk_to_ip(decoder); if (err) @@ -2004,6 +2040,7 @@ static int intel_pt_sync(struct intel_pt_decoder *decoder) decoder->pge = false; decoder->continuous_period = false; + decoder->have_last_ip = false; decoder->last_ip = 0; decoder->ip = 0; intel_pt_clear_stack(&decoder->stack); @@ -2012,6 +2049,7 @@ static int intel_pt_sync(struct intel_pt_decoder *decoder) if (err) return err; + decoder->have_last_ip = true; decoder->pkt_state = INTEL_PT_STATE_NO_IP; err = intel_pt_walk_psb(decoder); @@ -2030,7 +2068,7 @@ static int intel_pt_sync(struct intel_pt_decoder *decoder) static uint64_t intel_pt_est_timestamp(struct intel_pt_decoder *decoder) { - uint64_t est = decoder->timestamp_insn_cnt << 1; + uint64_t est = decoder->sample_insn_cnt << 1; if (!decoder->cbr || !decoder->max_non_turbo_ratio) goto out; @@ -2038,7 +2076,7 @@ static uint64_t intel_pt_est_timestamp(struct intel_pt_decoder *decoder) est *= decoder->max_non_turbo_ratio; est /= decoder->cbr; out: - return decoder->timestamp + est; + return decoder->sample_timestamp + est; } const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder) @@ -2054,7 +2092,9 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder) err = intel_pt_sync(decoder); break; case INTEL_PT_STATE_NO_IP: + decoder->have_last_ip = false; decoder->last_ip = 0; + decoder->ip = 0; /* Fall through */ case INTEL_PT_STATE_ERR_RESYNC: err = intel_pt_sync_ip(decoder); @@ -2091,15 +2131,24 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder) } } while (err == -ENOLINK); - decoder->state.err = err ? intel_pt_ext_err(err) : 0; - decoder->state.timestamp = decoder->timestamp; + if (err) { + decoder->state.err = intel_pt_ext_err(err); + decoder->state.from_ip = decoder->ip; + decoder->sample_timestamp = decoder->timestamp; + decoder->sample_insn_cnt = decoder->timestamp_insn_cnt; + } else { + decoder->state.err = 0; + if (intel_pt_sample_time(decoder->pkt_state)) { + decoder->sample_timestamp = decoder->timestamp; + decoder->sample_insn_cnt = decoder->timestamp_insn_cnt; + } + } + + decoder->state.timestamp = decoder->sample_timestamp; decoder->state.est_timestamp = intel_pt_est_timestamp(decoder); decoder->state.cr3 = decoder->cr3; decoder->state.tot_insn_cnt = decoder->tot_insn_cnt; - if (err) - decoder->state.from_ip = decoder->ip; - return &decoder->state; } diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c index b1257c816310..7528ae4f7e28 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c @@ -17,6 +17,7 @@ #include <string.h> #include <endian.h> #include <byteswap.h> +#include <linux/compiler.h> #include "intel-pt-pkt-decoder.h" @@ -292,36 +293,46 @@ static int intel_pt_get_ip(enum intel_pt_pkt_type type, unsigned int byte, const unsigned char *buf, size_t len, struct intel_pt_pkt *packet) { - switch (byte >> 5) { + int ip_len; + + packet->count = byte >> 5; + + switch (packet->count) { case 0: - packet->count = 0; + ip_len = 0; break; case 1: if (len < 3) return INTEL_PT_NEED_MORE_BYTES; - packet->count = 2; + ip_len = 2; packet->payload = le16_to_cpu(*(uint16_t *)(buf + 1)); break; case 2: if (len < 5) return INTEL_PT_NEED_MORE_BYTES; - packet->count = 4; + ip_len = 4; packet->payload = le32_to_cpu(*(uint32_t *)(buf + 1)); break; case 3: - case 6: + case 4: if (len < 7) return INTEL_PT_NEED_MORE_BYTES; - packet->count = 6; + ip_len = 6; memcpy_le64(&packet->payload, buf + 1, 6); break; + case 6: + if (len < 9) + return INTEL_PT_NEED_MORE_BYTES; + ip_len = 8; + packet->payload = le64_to_cpu(*(uint64_t *)(buf + 1)); + break; default: return INTEL_PT_BAD_PACKET; } packet->type = type; - return packet->count + 1; + return ip_len + 1; } static int intel_pt_get_mode(const unsigned char *buf, size_t len, @@ -488,6 +499,7 @@ int intel_pt_pkt_desc(const struct intel_pt_pkt *packet, char *buf, case INTEL_PT_FUP: if (!(packet->count)) return snprintf(buf, buf_len, "%s no ip", name); + __fallthrough; case INTEL_PT_CYC: case INTEL_PT_VMCS: case INTEL_PT_MTC: diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 854dd2105bd5..881bbb5e7912 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -138,11 +138,11 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = { #define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE) #define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT) -#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ - while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ - if (sys_dirent.d_type == DT_DIR && \ - (strcmp(sys_dirent.d_name, ".")) && \ - (strcmp(sys_dirent.d_name, ".."))) +#define for_each_subsystem(sys_dir, sys_dirent) \ + while ((sys_dirent = readdir(sys_dir)) != NULL) \ + if (sys_dirent->d_type == DT_DIR && \ + (strcmp(sys_dirent->d_name, ".")) && \ + (strcmp(sys_dirent->d_name, ".."))) static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) { @@ -159,12 +159,12 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) return 0; } -#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \ - while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ - if (evt_dirent.d_type == DT_DIR && \ - (strcmp(evt_dirent.d_name, ".")) && \ - (strcmp(evt_dirent.d_name, "..")) && \ - (!tp_event_has_id(&sys_dirent, &evt_dirent))) +#define for_each_event(sys_dirent, evt_dir, evt_dirent) \ + while ((evt_dirent = readdir(evt_dir)) != NULL) \ + if (evt_dirent->d_type == DT_DIR && \ + (strcmp(evt_dirent->d_name, ".")) && \ + (strcmp(evt_dirent->d_name, "..")) && \ + (!tp_event_has_id(sys_dirent, evt_dirent))) #define MAX_EVENT_LENGTH 512 @@ -173,7 +173,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) { struct tracepoint_path *path = NULL; DIR *sys_dir, *evt_dir; - struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; + struct dirent *sys_dirent, *evt_dirent; char id_buf[24]; int fd; u64 id; @@ -184,18 +184,18 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) if (!sys_dir) return NULL; - for_each_subsystem(sys_dir, sys_dirent, sys_next) { + for_each_subsystem(sys_dir, sys_dirent) { snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, - sys_dirent.d_name); + sys_dirent->d_name); evt_dir = opendir(dir_path); if (!evt_dir) continue; - for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { + for_each_event(sys_dirent, evt_dir, evt_dirent) { snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path, - evt_dirent.d_name); + evt_dirent->d_name); fd = open(evt_path, O_RDONLY); if (fd < 0) continue; @@ -220,9 +220,9 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) free(path); return NULL; } - strncpy(path->system, sys_dirent.d_name, + strncpy(path->system, sys_dirent->d_name, MAX_EVENT_LENGTH); - strncpy(path->name, evt_dirent.d_name, + strncpy(path->name, evt_dirent->d_name, MAX_EVENT_LENGTH); return path; } @@ -1662,7 +1662,7 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, bool name_only) { DIR *sys_dir, *evt_dir; - struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; + struct dirent *sys_dirent, *evt_dirent; char evt_path[MAXPATHLEN]; char dir_path[MAXPATHLEN]; char **evt_list = NULL; @@ -1680,20 +1680,20 @@ restart: goto out_close_sys_dir; } - for_each_subsystem(sys_dir, sys_dirent, sys_next) { + for_each_subsystem(sys_dir, sys_dirent) { if (subsys_glob != NULL && - !strglobmatch(sys_dirent.d_name, subsys_glob)) + !strglobmatch(sys_dirent->d_name, subsys_glob)) continue; snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, - sys_dirent.d_name); + sys_dirent->d_name); evt_dir = opendir(dir_path); if (!evt_dir) continue; - for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { + for_each_event(sys_dirent, evt_dir, evt_dirent) { if (event_glob != NULL && - !strglobmatch(evt_dirent.d_name, event_glob)) + !strglobmatch(evt_dirent->d_name, event_glob)) continue; if (!evt_num_known) { @@ -1702,7 +1702,7 @@ restart: } snprintf(evt_path, MAXPATHLEN, "%s:%s", - sys_dirent.d_name, evt_dirent.d_name); + sys_dirent->d_name, evt_dirent->d_name); evt_list[evt_i] = strdup(evt_path); if (evt_list[evt_i] == NULL) @@ -1755,7 +1755,7 @@ out_close_sys_dir: int is_valid_tracepoint(const char *event_string) { DIR *sys_dir, *evt_dir; - struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; + struct dirent *sys_dirent, *evt_dirent; char evt_path[MAXPATHLEN]; char dir_path[MAXPATHLEN]; @@ -1763,17 +1763,17 @@ int is_valid_tracepoint(const char *event_string) if (!sys_dir) return 0; - for_each_subsystem(sys_dir, sys_dirent, sys_next) { + for_each_subsystem(sys_dir, sys_dirent) { snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, - sys_dirent.d_name); + sys_dirent->d_name); evt_dir = opendir(dir_path); if (!evt_dir) continue; - for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { + for_each_event(sys_dirent, evt_dir, evt_dirent) { snprintf(evt_path, MAXPATHLEN, "%s:%s", - sys_dirent.d_name, evt_dirent.d_name); + sys_dirent->d_name, evt_dirent->d_name); if (!strcmp(evt_path, event_string)) { closedir(evt_dir); closedir(sys_dir); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 6f2a0279476c..593066c68e3d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -153,7 +153,7 @@ static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *n if (fd == -1) return -1; - sret = read(fd, alias->unit, UNIT_MAX_LEN); + sret = read(fd, alias->unit, UNIT_MAX_LEN); if (sret < 0) goto error; diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scripting-engines/Build index 6516e220c247..82d28c67e0f3 100644 --- a/tools/perf/util/scripting-engines/Build +++ b/tools/perf/util/scripting-engines/Build @@ -1,6 +1,6 @@ libperf-$(CONFIG_LIBPERL) += trace-event-perl.o libperf-$(CONFIG_LIBPYTHON) += trace-event-python.o -CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-undef -Wno-switch-default +CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-nested-externs -Wno-undef -Wno-switch-default CFLAGS_trace-event-python.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index bcae659b6546..efb53772e0ec 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -269,6 +269,7 @@ static int strfilter_node__sprint(struct strfilter_node *node, char *buf) len = strfilter_node__sprint_pt(node->l, buf); if (len < 0) return len; + __fallthrough; case '!': if (buf) { *(buf + len++) = *node->p; diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index fc8781de62db..accb7ece1d3c 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -21,6 +21,8 @@ s64 perf_atoll(const char *str) case 'b': case 'B': if (*p) goto out_err; + + __fallthrough; case '\0': return length; default: diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 27ae382feb2d..7c97ecaeae48 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -488,6 +488,12 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) break; } else { int n = namesz + descsz; + + if (n > (int)sizeof(bf)) { + n = sizeof(bf); + pr_debug("%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n", + __func__, filename, nhdr.n_namesz, nhdr.n_descsz); + } if (read(fd, bf, n) != n) break; } diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 0a9ae8014729..829508a21448 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -227,7 +227,7 @@ void thread__find_cpumode_addr_location(struct thread *thread, struct addr_location *al) { size_t i; - const u8 const cpumodes[] = { + const u8 cpumodes[] = { PERF_RECORD_MISC_USER, PERF_RECORD_MISC_KERNEL, PERF_RECORD_MISC_GUEST_USER, diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 6ec3c5ca438f..4e666b95b87e 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -92,8 +92,8 @@ struct thread_map *thread_map__new_by_uid(uid_t uid) { DIR *proc; int max_threads = 32, items, i; - char path[256]; - struct dirent dirent, *next, **namelist = NULL; + char path[NAME_MAX + 1 + 6]; + struct dirent *dirent, **namelist = NULL; struct thread_map *threads = thread_map__alloc(max_threads); if (threads == NULL) @@ -106,16 +106,16 @@ struct thread_map *thread_map__new_by_uid(uid_t uid) threads->nr = 0; atomic_set(&threads->refcnt, 1); - while (!readdir_r(proc, &dirent, &next) && next) { + while ((dirent = readdir(proc)) != NULL) { char *end; bool grow = false; struct stat st; - pid_t pid = strtol(dirent.d_name, &end, 10); + pid_t pid = strtol(dirent->d_name, &end, 10); if (*end) /* only interested in proper numerical dirents */ continue; - snprintf(path, sizeof(path), "/proc/%s", dirent.d_name); + snprintf(path, sizeof(path), "/proc/%s", dirent->d_name); if (stat(path, &st) != 0) continue; diff --git a/tools/testing/selftests/capabilities/test_execve.c b/tools/testing/selftests/capabilities/test_execve.c index 10a21a958aaf..763f37fecfb8 100644 --- a/tools/testing/selftests/capabilities/test_execve.c +++ b/tools/testing/selftests/capabilities/test_execve.c @@ -138,9 +138,6 @@ static void chdir_to_tmpfs(void) if (chdir(cwd) != 0) err(1, "chdir to private tmpfs"); - - if (umount2(".", MNT_DETACH) != 0) - err(1, "detach private tmpfs"); } static void copy_fromat_to(int fromfd, const char *fromname, const char *toname) @@ -248,7 +245,7 @@ static int do_tests(int uid, const char *our_path) err(1, "chown"); if (chmod("validate_cap_sgidnonroot", S_ISGID | 0710) != 0) err(1, "chmod"); -} + } capng_get_caps_process(); @@ -384,7 +381,7 @@ static int do_tests(int uid, const char *our_path) } else { printf("[RUN]\tNon-root +ia, sgidnonroot => i\n"); exec_other_validate_cap("./validate_cap_sgidnonroot", - false, false, true, false); + false, false, true, false); if (fork_wait()) { printf("[RUN]\tNon-root +ia, sgidroot => i\n"); diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index 1dd087da6f31..111e09c3f4bf 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -47,6 +47,22 @@ static struct vfio_group *kvm_vfio_group_get_external_user(struct file *filep) return vfio_group; } +static bool kvm_vfio_external_group_match_file(struct vfio_group *group, + struct file *filep) +{ + bool ret, (*fn)(struct vfio_group *, struct file *); + + fn = symbol_get(vfio_external_group_match_file); + if (!fn) + return false; + + ret = fn(group, filep); + + symbol_put(vfio_external_group_match_file); + + return ret; +} + static void kvm_vfio_group_put_external_user(struct vfio_group *vfio_group) { void (*fn)(struct vfio_group *); @@ -171,18 +187,13 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg) if (!f.file) return -EBADF; - vfio_group = kvm_vfio_group_get_external_user(f.file); - fdput(f); - - if (IS_ERR(vfio_group)) - return PTR_ERR(vfio_group); - ret = -ENOENT; mutex_lock(&kv->lock); list_for_each_entry(kvg, &kv->group_list, node) { - if (kvg->vfio_group != vfio_group) + if (!kvm_vfio_external_group_match_file(kvg->vfio_group, + f.file)) continue; list_del(&kvg->node); @@ -196,7 +207,7 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg) mutex_unlock(&kv->lock); - kvm_vfio_group_put_external_user(vfio_group); + fdput(f); kvm_vfio_update_coherency(dev); |
