diff options
252 files changed, 14581 insertions, 1026 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/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 d58b98f9a702..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" @@ -295,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/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/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/net/can/k61-can.txt b/Documentation/devicetree/bindings/net/can/k61-can.txt new file mode 100644 index 000000000000..57e85817a0a5 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/k61-can.txt @@ -0,0 +1,27 @@ +* Kinetis K61 CAN * + +This driver implements SPI slave protocol for Freescale K61 CAN controller. + +Required properties: + - compatible: Should be "fsl,k61". + - reg: Should contain SPI chip select. + - interrupt-parent: Should specify interrupt controller for the interrupt. + - interrupts: Should contain IRQ line for the CAN controller. + - reset-gpio: Reference to the GPIO connected to the reset input. + - pinctrl-names : Names corresponding to the numbered pinctrl states. + - pinctrl-0 : This explains the active state of the GPIO line. + - pinctrl-1 : This explains the suspend state of the GPIO line. + + +Example: + + can-controller@0 { + compatible = "fsl,k61"; + reg = <0>; + interrupt-parent = <&tlmm_pinmux>; + interrupts = <25 0>; + reset-gpio = <&tlmm_pinmux 11 0x1>; + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&can_rst_on>; + pinctrl-1 = <&can_rst_off>; + }; 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/power/supply/qcom/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt index 012368275db3..dc04942d6792 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt @@ -271,6 +271,14 @@ First Level Node - FG Gen3 device Definition: A boolean property that when defined holds SOC at 100% when the battery is full. +- qcom,linearize-soc + Usage: optional + Value type: <empty> + Definition: A boolean property that when defined linearizes SOC when + the SOC drops after charge termination monotonically to + improve the user experience. This is applicable only if + "qcom,hold-soc-while-full" is specified. + - qcom,ki-coeff-soc-dischg Usage: optional Value type: <prop-encoded-array> 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/soc/qcom/guest_shm.txt b/Documentation/devicetree/bindings/soc/qcom/guest_shm.txt new file mode 100644 index 000000000000..9491344c7b9f --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/guest_shm.txt @@ -0,0 +1,19 @@ +QVM Guest Shared Memory + +guest_shm is a device that enables linux as a guest operating system +to allocate shared memory between virtual machines and send notifications +of updates to other virtual machines. + +Required properties: +- compatible: Must be "qvm,guest_shm". +- interrupt-parent: Parent interrupt controller. +- interrupts: Should contain QVM interrupt. +- reg: Physical address of the guest factory and length. + +Example: + qvm,guest_shm { + compatible = "qvm,guest_shm"; + interrupt-parent = <&gic>; + interrupts = <6 4>; + reg = <0x1c050000 0x1000>; + }; 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..bfaca0cf9adf 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 @@ -186,6 +188,7 @@ qcom Qualcomm Technologies, Inc qemu QEMU, a generic and open source machine emulator and virtualizer qi Qi Hardware qnap QNAP Systems, Inc. +qvm BlackBerry Ltd radxa Radxa raidsonic RaidSonic Technology GmbH ralink Mediatek/Ralink Technology Corp. diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 1c3de8ccb400..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 \ @@ -229,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/fg-gen3-batterydata-ascent-3450mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi index c7cecbca3929..7705d01570ac 100644 --- a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi @@ -11,71 +11,71 @@ */ qcom,ascent_3450mah { - /* Ascent_with_connector_3450mAh_averaged_MasterSlave_Jan6th2017 */ + /* Ascent_wConn_Aging_3450mAh_averaged_MasterSlave_Jul11th2017 */ qcom,max-voltage-uv = <4350000>; qcom,fg-cc-cv-threshold-mv = <4340>; qcom,fastchg-current-ma = <3450>; qcom,batt-id-kohm = <60>; qcom,battery-beta = <3435>; - qcom,battery-type = "ascent_3450mah_averaged_masterslave_jan6th2017"; - qcom,checksum = <0x96AC>; - qcom,gui-version = "PMI8998GUI - 2.0.0.54"; + qcom,battery-type = "ascent_3450mah_averaged_masterslave_jul11th2017"; + qcom,checksum = <0x7C33>; + qcom,gui-version = "PMI8998GUI - 2.0.0.58"; qcom,fg-profile-data = [ - 9C 1F 85 05 - 82 0A 73 FC - 2B 1D 72 EA - EE 03 66 0C - C8 17 F4 22 - E0 45 1F 52 - 5C 00 00 00 - 10 00 00 00 - 00 00 4A C4 - C7 BC 48 C2 - 0F 00 08 00 - E1 DA 5D ED - 8D FD B2 F3 - 96 E2 A7 12 - 7E F4 0E 3B - 24 06 09 20 - 27 00 14 00 - 83 1F EE 05 - 1F 0A 45 FD - 6B 1D 53 E5 - EC 0B 31 14 - 44 18 49 23 - 18 45 A6 53 - 55 00 00 00 - 0E 00 00 00 - 00 00 61 CC - B7 C3 0F BC - 0F 00 00 00 - 92 00 5D ED - E3 06 E0 00 - 75 FD 9C 03 - 47 DB B3 22 - CB 33 CC FF - 07 10 00 00 - 99 0D 99 45 - 0F 00 40 00 - AB 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 + 8F 1F 94 05 + 73 0A 4A 06 + 27 1D 21 EA + 16 0A 3B 0C + 07 18 97 22 + A5 3C EC 4A + 5C 00 00 00 + 10 00 00 00 + 00 00 92 BC + CD BD 02 B4 + 11 00 08 00 + 69 DA AD 07 + 4B FD 19 FA + 1D 0C B0 0C + EB F3 78 3B + 24 06 09 20 + 27 00 14 00 + 7E 1F F2 05 + 19 0A 55 FD + 6C 1D C6 ED + 1A 12 FF 1D + 6F 18 EB 22 + B9 45 6F 52 + 55 00 00 00 + 0E 00 00 00 + 00 00 A1 D5 + 34 BA A0 CA + 0F 00 00 00 + 93 00 AD 07 + 8D FD F6 00 + BA 0D 5C 04 + B3 FC F4 1B + C3 33 CC FF + 07 10 00 00 + A4 0D 99 45 + 0F 00 40 00 + A4 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-smb138x.dtsi b/arch/arm/boot/dts/qcom/msm-smb138x.dtsi index df7d30210c19..c156e91dfcf9 100644 --- a/arch/arm/boot/dts/qcom/msm-smb138x.dtsi +++ b/arch/arm/boot/dts/qcom/msm-smb138x.dtsi @@ -18,7 +18,7 @@ compatible = "qcom,i2c-pmic"; reg = <0x8>; #address-cells = <1>; - #size-cells = <1>; + #size-cells = <0>; interrupt-parent = <&spmi_bus>; interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>; interrupt_names = "smb138x"; 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-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi index 316859a65801..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 @@ -2731,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 936dfd4d1cb2..1800a31dda9a 100644 --- a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi @@ -1986,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.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index f5e059484c95..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>; 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/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-v2.dtsi b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi index acdd4bdcd95b..abc0247b4475 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi @@ -293,7 +293,8 @@ < 1900800 1525 >; cpu-to-dev-map-4 = < 2112000 1525 >, - < 2496000 5195 >; + < 2342400 5195 >, + < 2496000 13763 >; }; }; 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/sdm630-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi index c24a41656f3a..af3c5d1b51da 100644 --- a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi @@ -399,9 +399,9 @@ &qusb_phy0 { qcom,qusb-phy-init-seq = <0xf8 0x80 - 0x80 0x84 + 0x83 0x84 0x83 0x88 - 0xc7 0x8c + 0xc3 0x8c 0x30 0x08 0x79 0x0c 0x21 0x10 diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi index 5618f02e34f2..e918864a3df7 100644 --- a/arch/arm/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630.dtsi @@ -1366,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>; 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..279a542be7e4 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-cdp.dtsi @@ -0,0 +1,27 @@ +/* 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" +/ { +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_nt35695b_truly_fhd_video>; +}; + +&mdss_dsi1 { + status = "disabled"; +}; 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..f1d96459ec17 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm636-mtp.dtsi @@ -0,0 +1,27 @@ +/* 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" +/ { +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_nt35695b_truly_fhd_video>; +}; + +&mdss_dsi1 { + status = "disabled"; +}; 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-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi index fecb86dcfdeb..2194cf606d29 100644 --- a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi @@ -506,6 +506,51 @@ qcom,bus-max = <0>; }; }; + + qcom,gpu-pwrlevels-4 { + #address-cells = <1>; + #size-cells = <0>; + + qcom,speed-bin = <78>; + + qcom,initial-pwrlevel = <1>; + + /* SVS */ + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <370000000>; + qcom,bus-freq = <8>; + qcom,bus-min = <6>; + qcom,bus-max = <11>; + }; + + /* Low SVS */ + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <266000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <3>; + qcom,bus-max = <6>; + }; + + /* Min SVS */ + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <160000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + /* XO */ + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <19200000>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; + }; }; }; diff --git a/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi index e78c2474df4d..b1408cc295e8 100644 --- a/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi @@ -121,9 +121,9 @@ &qusb_phy0 { qcom,qusb-phy-init-seq = <0xf8 0x80 - 0x80 0x84 + 0x83 0x84 0x83 0x88 - 0xc7 0x8c + 0xc3 0x8c 0x30 0x08 0x79 0x0c 0x21 0x10 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 e00753f8b3e7..c436ce643091 100644 --- a/arch/arm/boot/dts/qcom/sdm660.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660.dtsi @@ -1254,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>; @@ -1283,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 >, @@ -1301,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>; @@ -1354,6 +1381,7 @@ < 1113600 >, < 1401600 >, < 1536000 >, + < 1612800 >, < 1747200 >, < 1843200 >; @@ -1361,6 +1389,7 @@ < 1113600 >, < 1401600 >, < 1747200 >, + < 1804800 >, < 1958400 >, < 2150400 >, < 2208000 >, @@ -1577,7 +1606,6 @@ qcom,msm_fastrpc { compatible = "qcom,msm-fastrpc-adsp"; qcom,fastrpc-glink; - qcom,fastrpc-vmid-heap-shared; qcom,msm_fastrpc_compute_cb1 { compatible = "qcom,msm-fastrpc-compute-cb"; @@ -2056,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>; @@ -2130,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-agave.dtsi b/arch/arm/boot/dts/qcom/vplatform-lfv-agave.dtsi new file mode 100644 index 000000000000..fddffee703d1 --- /dev/null +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-agave.dtsi @@ -0,0 +1,50 @@ +/* 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. + */ + +&soc { + qcom,msm-dai-mi2s { + dai_mi2s_sec: qcom,msm-dai-q6-mi2s-sec { + qcom,msm-mi2s-rx-lines = <2>; + qcom,msm-mi2s-tx-lines = <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&sec_mi2s_active &sec_mi2s_sd0_active + &sec_mi2s_sd1_active>; + pinctrl-1 = <&sec_mi2s_sleep &sec_mi2s_sd0_sleep + &sec_mi2s_sd1_sleep>; + }; + + dai_mi2s: qcom,msm-dai-q6-mi2s-tert { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&tert_mi2s_active &tert_mi2s_sd0_active>; + pinctrl-1 = <&tert_mi2s_sleep &tert_mi2s_sd0_sleep>; + }; + + dai_mi2s_quat: qcom,msm-dai-q6-mi2s-quat { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&quat_mi2s_active &quat_mi2s_sd0_active>; + pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_sd0_sleep>; + }; + }; + + qcom,msm-dai-tdm-tert-rx { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&tert_tdm_dout_active>; + pinctrl-1 = <&tert_tdm_dout_sleep>; + }; + + qcom,msm-dai-tdm-quat-rx { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&quat_tdm_dout_active>; + pinctrl-1 = <&quat_tdm_dout_sleep>; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-pinctrl.dtsi b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-pinctrl.dtsi new file mode 100644 index 000000000000..ce7741f75b24 --- /dev/null +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-pinctrl.dtsi @@ -0,0 +1,538 @@ +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + tlmm: pinctrl@01010000 { + compatible = "qcom,msm8996-pinctrl"; + reg = <0x01010000 0x300000>; + interrupts = <0 208 0>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + + blsp1_uart2_active: blsp1_uart2_active { + mux { + pins = "gpio41", "gpio42", "gpio43", "gpio44"; + function = "blsp_uart2"; + }; + + config { + pins = "gpio41", "gpio42", "gpio43", "gpio44"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart2_sleep: blsp1_uart2_sleep { + mux { + pins = "gpio41", "gpio42", "gpio43", "gpio44"; + function = "gpio"; + }; + + config { + pins = "gpio41", "gpio42", "gpio43", "gpio44"; + drive-strength = <2>; + bias-disable; + }; + }; + + usb_hub_reset_active: usb_hub_reset_active { + usb_hub_reset_active { + pins = "gpio103"; + drive-strength = <8>; /* 8 mA */ + bias-pull-up; /* pull up */ + output-high; + }; + }; + + usb_hub_reset_suspend: usb_hub_reset_suspend { + usb_hub_reset_suspend { + pins = "gpio103"; + drive-strength = <2>; /* 2 mA */ + bias-disable= <0>; /* no pull */ + }; + }; + + i2c_6 { + i2c_6_active: i2c_6_active { + mux { + pins = "gpio27", "gpio28"; + function = "blsp_i2c6"; + }; + + config { + pins = "gpio27", "gpio28"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_6_sleep: i2c_6_sleep { + mux { + pins = "gpio27", "gpio28"; + function = "blsp_i2c6"; + }; + + config { + pins = "gpio27", "gpio28"; + 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 = <4>; + bias-disable; + }; + }; + + i2c_8_sleep: i2c_8_sleep { + mux { + pins = "gpio6", "gpio7"; + function = "blsp_i2c8"; + }; + + config { + pins = "gpio6", "gpio7"; + drive-strength = <4>; + bias-pull-up; + }; + }; + }; + + 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; + }; + }; + }; + + cnss_pins { + cnss_bootstrap_active: cnss_bootstrap_active { + mux { + pins = "gpio46"; + function = "gpio"; + }; + + 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; + }; + }; + }; + + sec_mi2s { + sec_mi2s_sleep: sec_mi2s_sleep { + mux { + pins = "gpio80", "gpio81"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio80", "gpio81"; + drive-strength = <2>; /* 2 mA */ + bias-disable; /* NO PULL */ + }; + }; + 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 = "sec_mi2s"; + }; + + config { + pins = "gpio82"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + 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 = "sec_mi2s"; + }; + + config { + pins = "gpio83"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + 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 { + tert_mi2s_sleep: tert_mi2s_sleep { + mux { + pins = "gpio75", "gpio76"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + 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 = "ter_mi2s"; + }; + + config { + pins = "gpio77"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + 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 */ + }; + }; + }; + + quat_mi2s { + quat_mi2s_sleep: quat_mi2s_sleep { + mux { + pins = "gpio58", "gpio59"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + 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 = "qua_mi2s"; + }; + + config { + pins = "gpio60"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + 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 */ + }; + }; + }; + + tert_tdm { + tert_tdm_sleep: tert_tdm_sleep { + mux { + pins = "gpio75", "gpio76"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + tert_tdm_active: tert_tdm_active { + mux { + pins = "gpio75", "gpio76"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + tert_tdm_din { + tert_tdm_din_sleep: tert_tdm_din_sleep { + mux { + pins = "gpio77"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio77"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + tert_tdm_din_active: tert_tdm_din_active { + mux { + pins = "gpio77"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio77"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_tdm_dout { + tert_tdm_dout_sleep: tert_tdm_dout_sleep { + mux { + pins = "gpio78"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio78"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + tert_tdm_dout_active: tert_tdm_dout_active { + mux { + pins = "gpio78"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio78"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_tdm { + quat_tdm_sleep: quat_tdm_sleep { + mux { + pins = "gpio58", "gpio59"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + quat_tdm_active: quat_tdm_active { + mux { + pins = "gpio58", "gpio59"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + quat_tdm_din { + quat_tdm_din_sleep: quat_tdm_din_sleep { + mux { + pins = "gpio60"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio60"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + quat_tdm_din_active: quat_tdm_din_active { + mux { + pins = "gpio60"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio60"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_tdm_dout { + quat_tdm_dout_sleep: quat_tdm_dout_sleep { + mux { + pins = "gpio61"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio61"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + quat_tdm_dout_active: quat_tdm_dout_active { + mux { + pins = "gpio61"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio61"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts index 5c0bbbccafea..e6d9f7b7d2f2 100644 --- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts @@ -13,12 +13,20 @@ /dts-v1/; #include "skeleton64.dtsi" +#include <dt-bindings/clock/msm-clocks-8996.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> / { model = "Qualcomm Technologies, Inc. MSM 8996"; compatible = "qcom,msm8996"; qcom,msm-id = <246 0x0>; + aliases { + spi9 = &spi_9; + i2c6 = &i2c_6; + i2c8 = &i2c_8; + }; + soc: soc { }; psci { @@ -53,14 +61,33 @@ reg = <0 0xc8000000 0 0x00400000>; label = "ion_audio_mem"; }; + modem_mem: modem_region@88800000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x88800000 0 0x6200000>; + }; + peripheral_mem: peripheral_region@8ea00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x8ea00000 0 0x2b00000>; + }; + adsp_mem: adsp_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; + reusable; + alignment = <0 0x100000>; + size = <0 0x400000>; + }; }; }; #include "vplatform-lfv-ion.dtsi" +#include "vplatform-lfv-smmu.dtsi" &soc { #address-cells = <1>; #size-cells = <1>; + virtual-interrupt-parent = "gic"; ranges = <0 0 0 0xffffffff>; compatible = "simple-bus"; @@ -125,11 +152,17 @@ asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; }; + qcom,msm-adsp-loader { + status = "ok"; + compatible = "qcom,adsp-loader"; + qcom,adsp-state = <0>; + }; qcom,msm-audio-ion { compatible = "qcom,msm-audio-ion"; + qcom,smmu-version = <2>; qcom,smmu-enabled; - qcom,smmu-sid = <1>; + iommus = <&lpass_q6_smmu 1>; }; pcm0: qcom,msm-pcm { @@ -587,4 +620,580 @@ hostless: qcom,msm-pcm-hostless { compatible = "qcom,msm-pcm-hostless"; }; + + qcom,msm-adsprpc-mem { + compatible = "qcom,msm-adsprpc-mem-region"; + memory-region = <&adsp_mem>; + }; + + qcom,msm_fastrpc { + compatible = "qcom,msm-fastrpc-adsp"; + + qcom,msm_fastrpc_compute_cb1 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&lpass_q6_smmu 8>; + }; + qcom,msm_fastrpc_compute_cb2 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&lpass_q6_smmu 9>; + }; + qcom,msm_fastrpc_compute_cb3 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&lpass_q6_smmu 10>; + }; + qcom,msm_fastrpc_compute_cb4 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&lpass_q6_smmu 11>; + }; + qcom,msm_fastrpc_compute_cb5 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&lpass_q6_smmu 12>; + }; + qcom,msm_fastrpc_compute_cb6 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&lpass_q6_smmu 5>; + }; + qcom,msm_fastrpc_compute_cb7 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&lpass_q6_smmu 6>; + }; + qcom,msm_fastrpc_compute_cb8 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&lpass_q6_smmu 7>; + }; + }; +}; + +#include "vplatform-lfv-msm8996-pinctrl.dtsi" +#include "msm8996-smp2p.dtsi" + +&soc { + qcom,ipc-spinlock@740000 { + compatible = "qcom,ipc-spinlock-sfpb"; + reg = <0x740000 0x8000>; + qcom,num-locks = <8>; + }; + + qcom,smem@86000000 { + compatible = "qcom,smem"; + reg = <0x86000000 0x200000>, + <0x9820010 0x4>, + <0x7b4000 0x8>; + reg-names = "smem", "irq-reg-base", + "smem_targ_info_reg"; + qcom,mpu-enabled; + + qcom,smd-modem { + compatible = "qcom,smd"; + qcom,smd-edge = <0>; + qcom,smd-irq-offset = <0x0>; + qcom,smd-irq-bitmask = <0x1000>; + interrupts = <0 449 1>; + label = "modem"; + qcom,not-loadable; + }; + + qcom,smd-adsp { + compatible = "qcom,smd"; + qcom,smd-edge = <0x1>; + qcom,smd-irq-offset = <0x0>; + qcom,smd-irq-bitmask = <0x100>; + interrupts = <0x0 0x9c 0x1>; + label = "adsp"; + }; + }; + + qcom,rmtfs_sharedmem@0 { + compatible = "qcom,sharedmem-uio"; + reg = <0x85e00000 0x00200000>; + reg-names = "rmtfs"; + qcom,client-id = <0x00000001>; + }; + + qcom,glink-smem-native-xprt-modem@86000000 { + compatible = "qcom,glink-smem-native-xprt"; + reg = <0x86000000 0x200000>, + <0x9820010 0x4>; + reg-names = "smem", "irq-reg-base"; + qcom,irq-mask = <0x8000>; + interrupts = <0 452 1>; + label = "mpss"; + }; + + qcom,glink-smem-native-xprt-adsp@86000000 { + compatible = "qcom,glink-smem-native-xprt"; + reg = <0x86000000 0x200000>, + <0x9820010 0x4>; + reg-names = "smem", "irq-reg-base"; + qcom,irq-mask = <0x200>; + interrupts = <0 157 1>; + label = "lpass"; + qcom,qos-config = <0x1b8>; + qcom,ramp-time = <0xaf>; + }; + + qcom,glink-qos-config-adsp { + compatible = "qcom,glink-qos-config"; + qcom,flow-info = <0x3c 0x0 0x3c 0x0 0x3c 0x0 0x3c 0x0>; + qcom,mtu-size = <0x800>; + qcom,tput-stats-cycle = <0xa>; + linux,phandle = <0x1b8>; + phandle = <0x1b8>; + }; + + /* IPC router */ + qcom,ipc_router { + compatible = "qcom,ipc_router"; + qcom,node-id = <1>; + }; + + qcom,ipc_router_modem_xprt { + compatible = "qcom,ipc_router_glink_xprt"; + qcom,ch-name = "IPCRTR"; + qcom,xprt-remote = "mpss"; + qcom,glink-xprt = "smd_trans"; + qcom,xprt-linkid = <1>; + qcom,xprt-version = <1>; + qcom,fragmented-data; + }; + + qcom,ipc_router_q6_xprt { + compatible = "qcom,ipc_router_glink_xprt"; + qcom,ch-name = "IPCRTR"; + qcom,xprt-remote = "lpass"; + qcom,glink-xprt = "smd_trans"; + qcom,xprt-linkid = <1>; + qcom,xprt-version = <1>; + qcom,fragmented-data; + }; + + /* IPA including NDP-BAM */ + ipa_hw: qcom,ipa@680000 { + compatible = "qcom,ipa"; + reg = <0x680000 0x4effc>, + <0x684000 0x26934>; + reg-names = "ipa-base", "bam-base"; + interrupts = <0 333 0>, + <0 432 0>; + interrupt-names = "ipa-irq", "bam-irq"; + qcom,ipa-hw-ver = <5>; /* IPA core version = IPAv2.5 */ + qcom,ipa-hw-mode = <0>; + qcom,ee = <0>; + qcom,use-ipa-tethering-bridge; + qcom,ipa-bam-remote-mode; + qcom,modem-cfg-emb-pipe-flt; + clocks = <&clock_gcc clk_ipa_clk>; + clock-names = "core_clk"; + qcom,use-dma-zone; + qcom,msm-bus,name = "ipa"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <2>; + qcom,msm-bus,vectors-KBps = + <90 512 0 0>, <90 585 0 0>, /* No vote */ + <90 512 80000 640000>, <90 585 80000 640000>, /* SVS */ + <90 512 206000 960000>, <90 585 206000 960000>; /* PERF */ + qcom,bus-vector-names = "MIN", "SVS", "PERF"; + }; + + /* rmnet over IPA */ + qcom,rmnet-ipa { + compatible = "qcom,rmnet-ipa"; + qcom,rmnet-ipa-ssr; + qcom,ipa-loaduC; + qcom,ipa-advertise-sg-support; + }; + + /* SPS */ + qcom,sps { + compatible = "qcom,msm_sps_4k"; + qcom,device-type = <3>; + qcom,pipe-attr-ee; + }; + + clock_gcc: qcom,gcc@300000 { + compatible = "qcom,dummycc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_mmss: qcom,mmsscc@8c0000 { + compatible = "qcom,dummycc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_gpu: qcom,gpucc@8c0000 { + compatible = "qcom,dummycc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_debug: qcom,cc-debug@362000 { + compatible = "qcom,dummycc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_cpu: qcom,cpu-clock-8996@ { + compatible = "qcom,dummycc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + pil_modem: qcom,mss@2080000 { + compatible = "qcom,pil-q6v55-mss"; + reg = <0x2080000 0x100>, + <0x0763000 0x008>, + <0x0765000 0x008>, + <0x0764000 0x008>, + <0x2180000 0x020>, + <0x038f008 0x004>; + reg-names = "qdsp6_base", "halt_q6", "halt_modem", + "halt_nc", "rmb_base", "restart_reg"; + + clocks = <&clock_gcc clk_cxo_clk_src>, + <&clock_gcc clk_gcc_mss_cfg_ahb_clk>, + <&clock_gcc clk_pnoc_clk>, + <&clock_gcc clk_gcc_mss_q6_bimc_axi_clk>, + <&clock_gcc clk_gcc_boot_rom_ahb_clk>, + <&clock_gcc clk_gpll0_out_msscc>, + <&clock_gcc clk_gcc_mss_snoc_axi_clk>, + <&clock_gcc clk_gcc_mss_mnoc_bimc_axi_clk>, + <&clock_gcc clk_qdss_clk>; + clock-names = "xo", "iface_clk", "pnoc_clk", "bus_clk", + "mem_clk", "gpll0_mss_clk", "snoc_axi_clk", + "mnoc_axi_clk", "qdss_clk"; + qcom,proxy-clock-names = "xo", "pnoc_clk", "qdss_clk"; + qcom,active-clock-names = "iface_clk", "bus_clk", "mem_clk", + "gpll0_mss_clk", "snoc_axi_clk", + "mnoc_axi_clk"; + + interrupts = <0 448 1>; + vdd_cx-supply = <&pm8994_s1_corner>; + vdd_cx-voltage = <7>; + vdd_mx-supply = <&pm8994_s2_corner>; + vdd_mx-uV = <6>; + vdd_pll-supply = <&pm8994_l12>; + qcom,vdd_pll = <1800000>; + qcom,firmware-name = "modem"; + qcom,pil-self-auth; + qcom,sysmon-id = <0>; + qcom,ssctl-instance-id = <0x12>; + qcom,override-acc; + qcom,ahb-clk-vote; + qcom,pnoc-clk-vote; + qcom,qdsp6v56-1-5; + qcom,mx-spike-wa; + memory-region = <&modem_mem>; + qcom,mem-protect-id = <0xF>; + + /* GPIO inputs from mss */ + qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>; + qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_1_in 1 0>; + qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>; + qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_1_in 3 0>; + qcom,gpio-shutdown-ack = <&smp2pgpio_ssr_smp2p_1_in 7 0>; + + /* GPIO output to mss */ + qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>; + status = "ok"; + }; + + pm8994_s1_corner: regulator-s1-corner { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_s1_corner"; + regulator-min-microvolt = <1>; + regulator-max-microvolt = <7>; + }; + + pm8994_s1_floor_corner: regulator-s1-floor-corner { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_s1_floor_corner"; + }; + + pm8994_s1_corner_ao: regulator-s1-corner-ao { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_s1_corner_ao"; + }; + + pm8994_s2_corner: regulator-s2-corner { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_s2_corner"; + regulator-min-microvolt = <1>; + regulator-max-microvolt = <7>; + }; + + pm8994_s2_corner_ao: regulator-s2-corner-ao { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_s2_corner_ao"; + }; + + pm8994_l12: regulator-l12 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_l12"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pm8994_l30: regulator-l30 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_l30"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + }; + + pm8994_l18_pin_ctrl: regulator-l18-pin-ctrl { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_l18_pin_ctrl"; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2900000>; + qcom,init-voltage = <2700000>; + }; + + pm8994_l26_corner: regulator-l26-corner { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_l26_corner"; + }; + + pm8994_l26_floor_corner: regulator-l26-floor-corner { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_l26_floor_corner"; + }; + + pmi8994_boost_pin_ctrl: regulator-bst-pin-ctrl { + compatible = "qcom,stub-regulator"; + regulator-name = "pmi8994_boost_pin_ctrl"; + }; + + pm8994_s11: spm-regulator@3200 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_s11"; + }; + + pmi8994_s2: regulator@1700 { + compatible = "qcom,stub-regulator"; + regulator-name = "pmi8994_s2"; + }; + + pm8994_s3: regulator-s3 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_s3"; + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1300000>; + qcom,init-voltage = <1300000>; + }; + + pm8994_s4: regulator-s4 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8994_s4"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + }; + + pm8004_s2: regulator@1700 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8004_s2"; + }; + + spi_eth_vreg: spi_eth_phy_vreg { + compatible = "qcom,stub-regulator"; + regulator-name = "ethernet_phy"; + }; + + usb_otg_switch: usb-otg-switch { + compatible = "qcom,stub-regulator"; + regulator-name = "usb_otg_vreg"; + }; + + rome_vreg: rome_vreg { + compatible = "qcom,stub-regulator"; + regulator-name = "rome_vreg"; + }; + + wlan_en_vreg: wlan_en_vreg { + compatible = "qcom,stub-regulator"; + regulator-name = "wlan_en_vreg"; + }; + + hl7509_en_vreg: hl7509_en_vreg { + compatible = "qcom,stub-regulator"; + regulator-name = "hl7509_en_vreg"; + }; + + gdsc_mmagic_camss: qcom,gdsc@8c3c4c { + compatible = "qcom,stub-regulator"; + regulator-name = "gdsc_mmagic_camss"; + regulator-min-microvolt = <1>; + regulator-max-microvolt = <7>; + }; + + gdsc_hlos1_vote_lpass_adsp: qcom,gdsc@37d034 { + compatible = "qcom,stub-regulator"; + regulator-name = "gdsc_hlos1_vote_lpass_adsp"; + regulator-min-microvolt = <1>; + regulator-max-microvolt = <7>; + }; + + spi_9: spi@75B7000 { /* BLSP2 QUP3 */ + compatible = "qcom,spi-qup-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "spi_physical"; + reg = <0x075B7000 0x600>; + interrupt-names = "spi_irq"; + interrupts = <0 103 0>; + spi-max-frequency = <19200000>; + qcom,infinite-mode = <0>; + qcom,ver-reg-exists; + qcom,master-id = <84>; + qcom,use-pinctrl; + pinctrl-names = "spi_default", "spi_sleep"; + pinctrl-0 = <&spi_9_active>; + pinctrl-1 = <&spi_9_sleep>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>, + <&clock_gcc clk_gcc_blsp2_qup3_spi_apps_clk>; + }; + + i2c_6: i2c@757a000 { /* BLSP1 QUP6 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x757a000 0x1000>; + reg-names = "qup_phys_addr"; + interrupt-names = "qup_irq"; + interrupts = <0 100 0>; + qcom,disable-dma; + qcom,master-id = <86>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>, + <&clock_gcc clk_gcc_blsp1_qup6_i2c_apps_clk>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_6_active>; + pinctrl-1 = <&i2c_6_sleep>; + }; + + i2c_8: i2c@75b6000 { /* BLSP2 QUP2 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr"; + reg = <0x75b6000 0x1000>; + interrupt-names = "qup_irq"; + interrupts = <0 102 0>; + qcom,disable-dma; + qcom,master-id = <84>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>, + <&clock_gcc clk_gcc_blsp2_qup2_i2c_apps_clk>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_8_active>; + pinctrl-1 = <&i2c_8_sleep>; + }; + + blsp1_uart2: uart@07570000 { /* BLSP1 UART2 */ + compatible = "qcom,msm-hsuart-v14"; + reg = <0x07570000 0x1000>, + <0x7544000 0x2b000>; + reg-names = "core_mem", "bam_mem"; + interrupt-names = "core_irq", "bam_irq", "wakeup_irq"; + interrupts = <0 108 0>, <0 238 0>, <0 810 0>; + #address-cells = <0>; + + qcom,inject-rx-on-wakeup; + qcom,rx-char-to-inject = <0xFD>; + + qcom,bam-tx-ep-pipe-index = <2>; + qcom,bam-rx-ep-pipe-index = <3>; + qcom,master-id = <86>; + clock-names = "core_clk", "iface_clk"; + clocks = <&clock_gcc clk_gcc_blsp1_uart2_apps_clk>, + <&clock_gcc clk_gcc_blsp1_ahb_clk>; + pinctrl-names = "sleep", "default"; + pinctrl-0 = <&blsp1_uart2_sleep>; + pinctrl-1 = <&blsp1_uart2_active>; + + qcom,msm-bus,name = "buart2"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <86 512 0 0>, + <86 512 500 800>; + }; + + qcom,lpass@9300000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x9300000 0x00100>; + interrupts = <0 162 1>; + + vdd_cx-supply = <&pm8994_s1_corner>; + qcom,proxy-reg-names = "vdd_cx"; + qcom,vdd_cx-uV-uA = <7 100000>; + + clocks = <&clock_gcc clk_cxo_pil_lpass_clk>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <1>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <423>; + qcom,sysmon-id = <1>; + qcom,ssctl-instance-id = <0x14>; + qcom,firmware-name = "adsp"; + qcom,edge = "lpass"; + memory-region = <&peripheral_mem>; + + /* GPIO inputs from lpass */ + qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>; + qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>; + qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>; + qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_2_in 3 0>; + + /* GPIO output to lpass */ + qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>; + }; + + qcom,cnss { + compatible = "qcom,cnss"; + wlan-bootstrap-gpio = <&tlmm 46 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 = "bootstrap_active", "bootstrap_sleep"; + pinctrl-0 = <&cnss_bootstrap_active>; + pinctrl-1 = <&cnss_bootstrap_sleep>; + + qcom,msm-bus,name = "msm-cnss"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + /* No vote */ + <45 512 0 0>, + /* Up to 200 Mbps */ + <45 512 41421 1520000>, + /* Up to 400 Mbps */ + <45 512 96650 1520000>, + /* Up to 800 Mbps */ + <45 512 207108 14432000>; + }; }; +#include "vplatform-lfv-agave.dtsi" diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-smmu.dtsi b/arch/arm/boot/dts/qcom/vplatform-lfv-smmu.dtsi new file mode 100644 index 000000000000..65eaa0c5aef9 --- /dev/null +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-smmu.dtsi @@ -0,0 +1,75 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm-arm-smmu.dtsi" +#include <dt-bindings/msm/msm-bus-ids.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +&lpass_q6_smmu { + status = "ok"; + qcom,register-save; + qcom,skip-init; + #global-interrupts = <1>; + interrupts = <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 393 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 394 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>; + vdd-supply = <&gdsc_hlos1_vote_lpass_adsp>; + clocks = <&clock_gcc clk_hlos1_vote_lpass_adsp_smmu_clk>; + clock-names = "lpass_q6_smmu_clocks"; + #clock-cells = <1>; +}; + +&cpp_fd_smmu { + status = "ok"; + qcom,register-save; + qcom,skip-init; + qcom,fatal-asf; + #global-interrupts = <1>; + interrupts = <GIC_SPI 264 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>; + vdd-supply = <&gdsc_mmagic_camss>; + clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>, + <&clock_mmss clk_mmss_mmagic_cfg_ahb_clk>, + <&clock_mmss clk_smmu_cpp_ahb_clk>, + <&clock_mmss clk_smmu_cpp_axi_clk>, + <&clock_mmss clk_mmagic_camss_axi_clk>; + clock-names = "mmagic_ahb_clk", "mmagic_cfg_ahb_clk", + "cpp_ahb_clk", "cpp_axi_clk", + "mmagic_camss_axi_clk"; + #clock-cells = <1>; + qcom,bus-master-id = <MSM_BUS_MASTER_CPP>; +}; + +&soc { + iommu_test_device { + compatible = "iommu-debug-test"; + /* + * 42 shouldn't be used by anyone on the cpp_fd_smmu. We just + * need _something_ here to get this node recognized by the + * SMMU driver. Our test uses ATOS, which doesn't use SIDs + * anyways, so using a dummy value is ok. + */ + iommus = <&cpp_fd_smmu 42>; + }; +}; diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig index 878e720a927b..fbd36cd00ea0 100644 --- a/arch/arm/configs/sdm660-perf_defconfig +++ b/arch/arm/configs/sdm660-perf_defconfig @@ -93,6 +93,7 @@ CONFIG_XFRM_USER=y CONFIG_XFRM_STATISTICS=y CONFIG_NET_KEY=y CONFIG_INET=y +CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y @@ -615,6 +616,9 @@ CONFIG_EXT4_ENCRYPTION=y CONFIG_EXT4_FS_ENCRYPTION=y CONFIG_EXT4_FS_ICE_ENCRYPTION=y CONFIG_EXT4_DEBUG=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y @@ -655,6 +659,7 @@ CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_QPDI=y CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig index 524abcf83e77..af5adfeb1a41 100644 --- a/arch/arm/configs/sdm660_defconfig +++ b/arch/arm/configs/sdm660_defconfig @@ -91,6 +91,7 @@ CONFIG_XFRM_USER=y CONFIG_XFRM_STATISTICS=y CONFIG_NET_KEY=y CONFIG_INET=y +CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y @@ -617,6 +618,9 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y CONFIG_EXT4_FS_ENCRYPTION=y CONFIG_EXT4_FS_ICE_ENCRYPTION=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y @@ -673,6 +677,7 @@ CONFIG_IRQSOFF_TRACER=y CONFIG_PREEMPT_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_CPU_FREQ_SWITCH_PROFILER=y +CONFIG_LKDTM=y CONFIG_MEMTEST=y CONFIG_PANIC_ON_DATA_CORRUPTION=y CONFIG_FREE_PAGES_RDONLY=y @@ -693,6 +698,7 @@ CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_QPDI=y CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 165c9a745d58..d61f3ae80e15 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -248,6 +248,15 @@ config PGTABLE_LEVELS default 3 if ARM64_16K_PAGES && ARM64_VA_BITS_47 default 4 if !ARM64_64K_PAGES && ARM64_VA_BITS_48 +config MSM_GVM_QUIN + bool "Enable virtualization Support for MSM kernel required for QUIN platform" + help + This enables support for MSM Kernel based virtual + machine for QUIN platform. + This helps to enable virtual driver support. + This should work on 64bit machine. + If you don't know what to do here, say N. + source "init/Kconfig" source "kernel/Kconfig.freezer" diff --git a/arch/arm64/configs/msm-auto-gvm-perf_defconfig b/arch/arm64/configs/msm-auto-gvm-perf_defconfig new file mode 100644 index 000000000000..2e551218af2d --- /dev/null +++ b/arch/arm64/configs/msm-auto-gvm-perf_defconfig @@ -0,0 +1,286 @@ +CONFIG_LOCALVERSION="-perf" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=15 +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_HMP=y +CONFIG_NAMESPACES=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_ARCH_QCOM=y +CONFIG_PCI=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_PREEMPT=y +CONFIG_ARM64_REG_REBALANCE_ON_CTX_SW=y +CONFIG_ZSMALLOC=y +CONFIG_BALANCE_ANON_FILE_RECLAIM=y +CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y +CONFIG_SECCOMP=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y +CONFIG_COMPAT=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_CAN=y +CONFIG_CAN_RH850=y +CONFIG_IPC_ROUTER=y +CONFIG_IPC_ROUTER_SECURITY=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_SRAM=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPPOE=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=m +CONFIG_SERIO_AMBAKMI=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MSM_V2=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=y +CONFIG_PINCTRL_MSM8996=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_WATCHDOG=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_DRM=y +# CONFIG_DRM_MSM is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_VIRTIO_INPUT=y +CONFIG_VIRTIO_MMIO=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA=y +CONFIG_RMNET_IPA=y +CONFIG_COMMON_CLK_MSM=y +CONFIG_MSM_CLK_CONTROLLER_V2=y +CONFIG_REMOTE_SPINLOCK_MSM=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_TESTS=y +CONFIG_MSM_SMEM=y +CONFIG_MSM_SMD=y +CONFIG_MSM_GLINK=y +CONFIG_MSM_GLINK_LOOPBACK_SERVER=y +CONFIG_MSM_GLINK_SMD_XPRT=y +CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y +CONFIG_MSM_SMEM_LOGGING=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_SCHEDSTATS=y +CONFIG_IPC_LOGGING=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_QMI_ENCDEC=y diff --git a/arch/arm64/configs/msm-auto-gvm_defconfig b/arch/arm64/configs/msm-auto-gvm_defconfig new file mode 100644 index 000000000000..a6d36c314a4a --- /dev/null +++ b/arch/arm64/configs/msm-auto-gvm_defconfig @@ -0,0 +1,316 @@ +CONFIG_SYSVIPC=y +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=15 +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_HMP=y +CONFIG_NAMESPACES=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_ARCH_QCOM=y +CONFIG_PCI=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_PREEMPT=y +CONFIG_ARM64_REG_REBALANCE_ON_CTX_SW=y +CONFIG_ZSMALLOC=y +CONFIG_BALANCE_ANON_FILE_RECLAIM=y +CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y +CONFIG_SECCOMP=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y +CONFIG_COMPAT=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_CAN=y +CONFIG_CAN_RH850=y +CONFIG_IPC_ROUTER=y +CONFIG_IPC_ROUTER_SECURITY=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_SRAM=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPPOE=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=m +CONFIG_SERIO_AMBAKMI=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MSM_V2=y +CONFIG_SPI=y +CONFIG_SPI_DEBUG=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=y +CONFIG_PINCTRL_MSM8996=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_WATCHDOG=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_DRM=y +# CONFIG_DRM_MSM is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_VIRTIO_INPUT=y +CONFIG_VIRTIO_MMIO=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA=y +CONFIG_RMNET_IPA=y +CONFIG_COMMON_CLK_MSM=y +CONFIG_MSM_CLK_CONTROLLER_V2=y +CONFIG_REMOTE_SPINLOCK_MSM=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y +CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y +CONFIG_MSM_SMEM=y +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_DEBUG=y +CONFIG_MSM_GLINK=y +CONFIG_MSM_GLINK_LOOPBACK_SERVER=y +CONFIG_MSM_GLINK_SMD_XPRT=y +CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y +CONFIG_MSM_SMEM_LOGGING=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_PAGE_OWNER=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_OBJECTS=y +CONFIG_DEBUG_OBJECTS_FREE=y +CONFIG_DEBUG_OBJECTS_TIMERS=y +CONFIG_DEBUG_OBJECTS_WORK=y +CONFIG_DEBUG_OBJECTS_RCU_HEAD=y +CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_PANIC_ON_SCHED_BUG=y +CONFIG_SCHEDSTATS=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_LIST=y +CONFIG_FAULT_INJECTION=y +CONFIG_FAIL_PAGE_ALLOC=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y +CONFIG_IPC_LOGGING=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_PANIC_ON_DATA_CORRUPTION=y +CONFIG_ARM64_PTDUMP=y +CONFIG_FREE_PAGES_RDONLY=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_QMI_ENCDEC=y 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/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 77157bb85ee1..3cbcc10ceb1d 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -698,6 +698,7 @@ CONFIG_IRQSOFF_TRACER=y CONFIG_PREEMPT_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_CPU_FREQ_SWITCH_PROFILER=y +CONFIG_LKDTM=y CONFIG_MEMTEST=y CONFIG_PANIC_ON_DATA_CORRUPTION=y CONFIG_ARM64_PTDUMP=y diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig index c92551c69bf8..0a5f7f1f0f2d 100644 --- a/arch/arm64/configs/sdm660-perf_defconfig +++ b/arch/arm64/configs/sdm660-perf_defconfig @@ -5,6 +5,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_RCU_FAST_NO_HZ=y CONFIG_RCU_NOCB_CPU=y @@ -93,6 +96,7 @@ CONFIG_XFRM_USER=y CONFIG_XFRM_STATISTICS=y CONFIG_NET_KEY=y CONFIG_INET=y +CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y @@ -242,6 +246,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y +CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -311,6 +316,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 @@ -559,6 +565,7 @@ CONFIG_QCOM_SCM=y CONFIG_QCOM_WATCHDOG_V2=y CONFIG_QCOM_IRQ_HELPER=y CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_QCOM_MINIDUMP=y CONFIG_ICNSS=y CONFIG_MSM_RUN_QUEUE_STATS=y CONFIG_MSM_BOOT_STATS=y @@ -610,6 +617,9 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y CONFIG_EXT4_FS_ENCRYPTION=y CONFIG_EXT4_FS_ICE_ENCRYPTION=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y @@ -642,6 +652,7 @@ CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_QPDI=y CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig index 6b45087e2603..7819916fb841 100644 --- a/arch/arm64/configs/sdm660_defconfig +++ b/arch/arm64/configs/sdm660_defconfig @@ -97,6 +97,7 @@ CONFIG_XFRM_USER=y CONFIG_XFRM_STATISTICS=y CONFIG_NET_KEY=y CONFIG_INET=y +CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y @@ -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 @@ -579,6 +581,7 @@ CONFIG_QCOM_SCM=y CONFIG_QCOM_WATCHDOG_V2=y CONFIG_QCOM_IRQ_HELPER=y CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_QCOM_MINIDUMP=y CONFIG_ICNSS=y CONFIG_MSM_GLADIATOR_ERP_V2=y CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y @@ -636,6 +639,9 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y CONFIG_EXT4_FS_ENCRYPTION=y CONFIG_EXT4_FS_ICE_ENCRYPTION=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y @@ -693,6 +699,7 @@ CONFIG_IRQSOFF_TRACER=y CONFIG_PREEMPT_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_CPU_FREQ_SWITCH_PROFILER=y +CONFIG_LKDTM=y CONFIG_MEMTEST=y CONFIG_PANIC_ON_DATA_CORRUPTION=y CONFIG_ARM64_PTDUMP=y @@ -715,6 +722,7 @@ CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_QPDI=y CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index ca522c822eff..961fd5d2e7ce 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -54,6 +54,7 @@ #include <asm/ptrace.h> #include <asm/virt.h> #include <asm/edac.h> +#include <soc/qcom/minidump.h> #define CREATE_TRACE_POINTS #include <trace/events/ipi.h> @@ -752,6 +753,7 @@ static void ipi_cpu_stop(unsigned int cpu, struct pt_regs *regs) pr_crit("CPU%u: stopping\n", cpu); show_regs(regs); dump_stack(); + dump_stack_minidump(regs->sp); arm64_check_cache_ecc(NULL); raw_spin_unlock(&stop_lock); } diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index a41178f8eeea..159c79612e63 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -3,6 +3,7 @@ * * Copyright (C) 2012 ARM Ltd. * Author: Catalin Marinas <catalin.marinas@arm.com> + * 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 as @@ -49,17 +50,6 @@ static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, return prot; } -static int __get_iommu_pgprot(struct dma_attrs *attrs, int prot, - bool coherent) -{ - if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs)) - prot |= IOMMU_NOEXEC; - if (coherent) - prot |= IOMMU_CACHE; - - return prot; -} - static bool is_dma_coherent(struct device *dev, struct dma_attrs *attrs) { bool is_coherent; @@ -930,7 +920,6 @@ static struct dma_map_ops iommu_dma_ops = { .sync_single_for_device = __iommu_sync_single_for_device, .sync_sg_for_cpu = __iommu_sync_sg_for_cpu, .sync_sg_for_device = __iommu_sync_sg_for_device, - .dma_supported = iommu_dma_supported, .mapping_error = iommu_dma_mapping_error, }; @@ -1145,6 +1134,17 @@ EXPORT_SYMBOL(arch_setup_dma_ops); #ifdef CONFIG_ARM64_DMA_USE_IOMMU +static int __get_iommu_pgprot(struct dma_attrs *attrs, int prot, + bool coherent) +{ + if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs)) + prot |= IOMMU_NOEXEC; + if (coherent) + prot |= IOMMU_CACHE; + + return prot; +} + /* * Make an area consistent for devices. * Note: Drivers should NOT use this function directly, as it will break diff --git a/drivers/base/core.c b/drivers/base/core.c index 5a56a8e9f006..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); 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/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c index 363b4692d228..7abd5598c47b 100644 --- a/drivers/bluetooth/btfm_slim_wcn3990.c +++ b/drivers/bluetooth/btfm_slim_wcn3990.c @@ -122,6 +122,18 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, 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; + } } /* Enable Tx port hw auto recovery for underrun or overrun error */ @@ -146,9 +158,15 @@ enable_disable_rxport: 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) BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index aba9010c5aa2..77c8f279b4f5 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1256,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); @@ -1269,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; } @@ -1337,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); + } } } @@ -1353,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); @@ -1386,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, @@ -2502,9 +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); - err = glink_queue_rx_intent(me->channel[cid].chan, NULL, 64); - if (err) - pr_info("adsprpc: initial intent failed for %d\n", 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)) diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index eaed3b101095..4111e599877a 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -3029,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) { @@ -3041,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"); @@ -3053,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); @@ -3194,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); @@ -3230,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 diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c index 74777212e4cf..10038e629e6c 100644 --- a/drivers/char/diag/diagfwd_cntl.c +++ b/drivers/char/diag/diagfwd_cntl.c @@ -1119,6 +1119,18 @@ void diag_map_pd_to_diagid(uint8_t pd, uint8_t *diag_id, int *peripheral) *diag_id = DIAG_ID_LPASS; *peripheral = PERIPHERAL_LPASS; break; + case PERIPHERAL_WCNSS: + *diag_id = 0; + *peripheral = PERIPHERAL_WCNSS; + break; + case PERIPHERAL_SENSORS: + *diag_id = 0; + *peripheral = PERIPHERAL_SENSORS; + break; + case PERIPHERAL_WDSP: + *diag_id = 0; + *peripheral = PERIPHERAL_WDSP; + break; case PERIPHERAL_CDSP: *diag_id = DIAG_ID_CDSP; *peripheral = PERIPHERAL_CDSP; diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index 78b8452b19b3..0f94bab3bf84 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -500,15 +500,29 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, temp_buf_main += (buf_len + 4); processed += buf_len; } + + if (flag_buf_1) { + fwd_info->cpd_len_1 = len_cpd; + if (fwd_info->type == TYPE_DATA) + fwd_info->upd_len_1_a = len_upd_1; + if (peripheral == PERIPHERAL_LPASS && + fwd_info->type == TYPE_DATA) + fwd_info->upd_len_2_a = len_upd_2; + } else if (flag_buf_2) { + fwd_info->cpd_len_2 = len_cpd; + if (fwd_info->type == TYPE_DATA) + fwd_info->upd_len_1_b = len_upd_1; + if (peripheral == PERIPHERAL_LPASS && + fwd_info->type == TYPE_DATA) + fwd_info->upd_len_2_b = len_upd_2; + } + if (peripheral == PERIPHERAL_LPASS && fwd_info->type == TYPE_DATA && len_upd_2) { - if (flag_buf_1) { - fwd_info->upd_len_2_a = len_upd_2; + if (flag_buf_1) temp_ptr_upd = fwd_info->buf_upd_2_a; - } else { - fwd_info->upd_len_2_b = len_upd_2; + else temp_ptr_upd = fwd_info->buf_upd_2_b; - } temp_ptr_upd->ctxt &= 0x00FFFFFF; temp_ptr_upd->ctxt |= (SET_PD_CTXT(ctxt_upd_2)); @@ -522,15 +536,10 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, fwd_info->upd_len_2_b = 0; } if (fwd_info->type == TYPE_DATA && len_upd_1) { - if (flag_buf_1) { - fwd_info->upd_len_1_a = - len_upd_1; + if (flag_buf_1) temp_ptr_upd = fwd_info->buf_upd_1_a; - } else { - fwd_info->upd_len_1_b = - len_upd_1; + else temp_ptr_upd = fwd_info->buf_upd_1_b; - } temp_ptr_upd->ctxt &= 0x00FFFFFF; temp_ptr_upd->ctxt |= (SET_PD_CTXT(ctxt_upd_1)); @@ -544,10 +553,6 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, fwd_info->upd_len_1_b = 0; } if (len_cpd) { - if (flag_buf_1) - fwd_info->cpd_len_1 = len_cpd; - else - fwd_info->cpd_len_2 = len_cpd; temp_ptr_cpd->ctxt &= 0x00FFFFFF; temp_ptr_cpd->ctxt |= (SET_PD_CTXT(ctxt_cpd)); @@ -1049,16 +1054,7 @@ 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); - - /* - * 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); + diagfwd_cntl_open(dest_info); init_fn(peripheral); mutex_unlock(&driver->diagfwd_channel_mutex[peripheral]); diagfwd_queue_read(&peripheral_info[TYPE_DATA][peripheral]); @@ -1251,15 +1247,11 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info) diagfwd_buffers_init(fwd_info); /* - * Initialize buffers for glink supported - * peripherals only. Open control channel to update - * masks after buffers are initialized. + * Initialize buffers for glink supported + * peripherals only. */ - if (fwd_info->transport == TRANSPORT_GLINK) { + 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); @@ -1345,12 +1337,33 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt) if (ctxt == 1 && fwd_info->buf_1) { /* Buffer 1 for core PD is freed */ - atomic_set(&fwd_info->buf_1->in_busy, 0); fwd_info->cpd_len_1 = 0; + + if (peripheral == PERIPHERAL_LPASS) { + if (!fwd_info->upd_len_1_a && + !fwd_info->upd_len_2_a) + atomic_set(&fwd_info->buf_1->in_busy, 0); + } else if (peripheral == PERIPHERAL_MODEM) { + if (!fwd_info->upd_len_1_a) + atomic_set(&fwd_info->buf_1->in_busy, 0); + } else { + atomic_set(&fwd_info->buf_1->in_busy, 0); + } } else if (ctxt == 2 && fwd_info->buf_2) { /* Buffer 2 for core PD is freed */ - atomic_set(&fwd_info->buf_2->in_busy, 0); fwd_info->cpd_len_2 = 0; + + if (peripheral == PERIPHERAL_LPASS) { + if (!fwd_info->upd_len_1_b && + !fwd_info->upd_len_2_b) + atomic_set(&fwd_info->buf_2->in_busy, 0); + } else if (peripheral == PERIPHERAL_MODEM) { + if (!fwd_info->upd_len_1_b) + atomic_set(&fwd_info->buf_2->in_busy, 0); + } else { + atomic_set(&fwd_info->buf_2->in_busy, 0); + } + } else if (ctxt == 3 && fwd_info->buf_upd_1_a) { /* Buffer 1 for user pd 1 is freed */ atomic_set(&fwd_info->buf_upd_1_a->in_busy, 0); diff --git a/drivers/clk/msm/clock-dummy.c b/drivers/clk/msm/clock-dummy.c index e5339b110cd6..caa6a6ab7565 100644 --- a/drivers/clk/msm/clock-dummy.c +++ b/drivers/clk/msm/clock-dummy.c @@ -64,12 +64,18 @@ struct clk dummy_clk = { static void *dummy_clk_dt_parser(struct device *dev, struct device_node *np) { struct clk *c; + u32 rate; + c = devm_kzalloc(dev, sizeof(*c), GFP_KERNEL); if (!c) { dev_err(dev, "failed to map memory for %s\n", np->name); return ERR_PTR(-ENOMEM); } c->ops = &clk_ops_dummy; + + if (!of_property_read_u32(np, "clock-frequency", &rate)) + c->rate = rate; + return msmclk_generic_clk_init(dev, np, c); } MSMCLK_PARSER(dummy_clk_dt_parser, "qcom,dummy-clk", 0); @@ -82,6 +88,7 @@ static struct clk *of_dummy_get(struct of_phandle_args *clkspec, static struct of_device_id msm_clock_dummy_match_table[] = { { .compatible = "qcom,dummycc" }, + { .compatible = "fixed-clock" }, {} }; diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index ddaeca1b29e4..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 @@ -623,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])) { @@ -644,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) @@ -678,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); @@ -773,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, 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 4c082fff2fc5..678b2178cb69 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -49,6 +49,7 @@ msm_drm-y := \ 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/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index c8b11425a817..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; } diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 49b6029c3342..ae3a930005b6 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -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 diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 5fa4c21060f9..2ca91674a15a 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -10,12 +10,13 @@ * 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" #include "sde_connector.h" #include "sde_backlight.h" +#include "sde_splash.h" #define SDE_DEBUG_CONN(c, fmt, ...) SDE_DEBUG("conn%d " fmt,\ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__) @@ -38,6 +39,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) { @@ -155,6 +163,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); @@ -353,6 +362,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, @@ -379,8 +438,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); @@ -404,12 +463,20 @@ 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); + 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) { @@ -501,13 +568,89 @@ void sde_connector_prepare_fence(struct drm_connector *connector) void sde_connector_complete_commit(struct drm_connector *connector) { + struct drm_device *dev; + struct msm_drm_private *priv; + struct sde_connector *c_conn; + struct sde_kms *sde_kms; + if (!connector) { SDE_ERROR("invalid connector\n"); return; } + dev = connector->dev; + priv = dev->dev_private; + sde_kms = to_sde_kms(priv->kms); + /* signal connector's retire fence */ sde_fence_signal(&to_sde_connector(connector)->retire_fence, 0); + + /* after first vsync comes, + * early splash resource should start to be released. + */ + if (sde_splash_get_lk_complete_status(&sde_kms->splash_info)) { + c_conn = to_sde_connector(connector); + + sde_splash_clean_up_free_resource(priv->kms, + &priv->phandle, + c_conn->connector_type, + c_conn->display); + } + +} + +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) @@ -558,7 +701,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, @@ -681,6 +824,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] = @@ -714,6 +862,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); @@ -793,6 +943,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"); @@ -819,6 +974,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 b76ce0aaf577..f9b8c3966d74 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -153,6 +153,20 @@ struct sde_connector_ops { */ 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); }; /** @@ -165,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 @@ -185,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]; @@ -361,5 +383,12 @@ bool sde_connector_mode_needs_full_range(struct drm_connector *connector); */ 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 2e9e2192670d..a0417a0dd12e 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -600,23 +600,14 @@ 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 || !crtc->dev) { + if (!crtc || !crtc->state) { 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)); @@ -625,22 +616,6 @@ 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); - } } /** @@ -935,6 +910,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) @@ -979,8 +963,10 @@ 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 void _sde_crtc_vblank_enable_nolock( +static int _sde_crtc_vblank_enable_no_lock( struct sde_crtc *sde_crtc, bool enable) { struct drm_device *dev; @@ -988,10 +974,11 @@ static void _sde_crtc_vblank_enable_nolock( 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; + return -EINVAL; } crtc = &sde_crtc->base; @@ -1000,13 +987,16 @@ static void _sde_crtc_vblank_enable_nolock( if (!priv->kms) { SDE_ERROR("invalid kms\n"); - return; + return -EINVAL; } sde_kms = to_sde_kms(priv->kms); if (enable) { - sde_power_resource_enable(&priv->phandle, + 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; @@ -1025,9 +1015,11 @@ static void _sde_crtc_vblank_enable_nolock( sde_encoder_register_vblank_callback(enc, NULL, NULL); } - sde_power_resource_enable(&priv->phandle, + ret = sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false); } + + return ret; } /** @@ -1073,8 +1065,8 @@ static void _sde_crtc_set_suspend(struct drm_crtc *crtc, bool enable) if (sde_crtc->suspend == enable) SDE_DEBUG("crtc%d suspend already set to %d, ignoring update\n", crtc->base.id, enable); - else if (atomic_read(&sde_crtc->vblank_refcount) != 0) - _sde_crtc_vblank_enable_nolock(sde_crtc, !enable); + else if (sde_crtc->enabled && sde_crtc->vblank_requested) + _sde_crtc_vblank_enable_no_lock(sde_crtc, !enable); sde_crtc->suspend = enable; @@ -1158,39 +1150,13 @@ static void sde_crtc_reset(struct drm_crtc *crtc) crtc->state = &cstate->base; } -static int _sde_crtc_vblank_no_lock(struct sde_crtc *sde_crtc, bool en) -{ - if (!sde_crtc) { - SDE_ERROR("invalid crtc\n"); - return -EINVAL; - } else if (en && atomic_inc_return(&sde_crtc->vblank_refcount) == 1) { - SDE_DEBUG("crtc%d vblank enable\n", sde_crtc->base.base.id); - if (!sde_crtc->suspend) - _sde_crtc_vblank_enable_nolock(sde_crtc, true); - } else if (!en && atomic_read(&sde_crtc->vblank_refcount) < 1) { - SDE_ERROR("crtc%d invalid vblank disable\n", - sde_crtc->base.base.id); - return -EINVAL; - } else if (!en && atomic_dec_return(&sde_crtc->vblank_refcount) == 0) { - SDE_DEBUG("crtc%d vblank disable\n", sde_crtc->base.base.id); - if (!sde_crtc->suspend) - _sde_crtc_vblank_enable_nolock(sde_crtc, false); - } else { - SDE_DEBUG("crtc%d vblank %s refcount:%d\n", - sde_crtc->base.base.id, - en ? "enable" : "disable", - atomic_read(&sde_crtc->vblank_refcount)); - } - - return 0; -} - static void sde_crtc_disable(struct drm_crtc *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 || !crtc->dev || !crtc->state) { SDE_ERROR("invalid crtc\n"); @@ -1210,17 +1176,19 @@ static void sde_crtc_disable(struct drm_crtc *crtc) _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_crtc->suspend) { - SDE_ERROR("crtc%d invalid vblank refcount\n", - crtc->base.id); - SDE_EVT32(DRMID(crtc), atomic_read(&sde_crtc->vblank_refcount)); - while (atomic_read(&sde_crtc->vblank_refcount)) - if (_sde_crtc_vblank_no_lock(sde_crtc, false)) - break; + 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", @@ -1255,6 +1223,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"); @@ -1283,6 +1252,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); @@ -1329,8 +1311,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"); @@ -1347,6 +1328,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 */ @@ -1380,11 +1365,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) @@ -1394,8 +1380,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) { @@ -1404,22 +1396,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); } @@ -1431,6 +1425,49 @@ 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; } @@ -1438,7 +1475,7 @@ end: int sde_crtc_vblank(struct drm_crtc *crtc, bool en) { struct sde_crtc *sde_crtc; - int rc; + int ret; if (!crtc) { SDE_ERROR("invalid crtc\n"); @@ -1447,10 +1484,19 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en) sde_crtc = to_sde_crtc(crtc); mutex_lock(&sde_crtc->crtc_lock); - rc = _sde_crtc_vblank_no_lock(sde_crtc, en); + 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 rc; + return 0; } void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, @@ -1551,7 +1597,7 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, sde_kms_info_add_keyint(info, "max_mdp_clk", sde_kms->perf.max_core_clk_rate); msm_property_set_blob(&sde_crtc->property_info, &sde_crtc->blob_info, - info->data, info->len, CRTC_PROP_INFO); + info->data, SDE_KMS_INFO_DATALEN(info), CRTC_PROP_INFO); kfree(info); } @@ -1763,8 +1809,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); @@ -1892,7 +1937,6 @@ 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); diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h index 6b8483d574b1..0eed61580cd8 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.h +++ b/drivers/gpu/drm/msm/sde/sde_crtc.h @@ -81,8 +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 @@ -117,8 +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 23fb79241d84..cb8b349e72c7 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -506,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]; @@ -523,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); 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 69a4237f7b67..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); } @@ -700,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; @@ -832,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 a185eb338134..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}, }; @@ -691,6 +695,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, 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); @@ -704,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, @@ -723,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); @@ -733,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); @@ -743,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); @@ -762,6 +784,8 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, 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); @@ -775,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; @@ -803,6 +835,8 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg, 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)++; @@ -819,6 +853,8 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg, 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); @@ -917,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); @@ -944,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; @@ -1033,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); @@ -1125,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; @@ -1211,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; @@ -1290,6 +1339,8 @@ 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); @@ -1515,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) { @@ -1585,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 */ @@ -1650,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); @@ -1777,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); } @@ -1796,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); } } @@ -1926,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 92dd829eee3e..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 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 cdf67c0aa864..8d821e43afa5 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -69,7 +69,7 @@ * * This is disabled by default. */ -static bool sdecustom = true; +static bool sdecustom; module_param(sdecustom, bool, 0400); MODULE_PARM_DESC(sdecustom, "Enable customizations for sde clients"); @@ -1167,6 +1167,44 @@ 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; @@ -1193,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"); @@ -1245,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) { diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h index d929e48a3fe8..dee16d119d47 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.h +++ b/drivers/gpu/drm/msm/sde/sde_kms.h @@ -134,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; @@ -281,10 +282,12 @@ struct sde_kms_info { /** * SDE_KMS_INFO_DATALEN - Macro for accessing sde_kms_info data length + * it adds an extra character length to count null. * @S: Pointer to sde_kms_info structure * Returns: Size of available byte data */ -#define SDE_KMS_INFO_DATALEN(S) ((S) ? ((struct sde_kms_info *)(S))->len : 0) +#define SDE_KMS_INFO_DATALEN(S) ((S) ? ((struct sde_kms_info *)(S))->len + 1 \ + : 0) /** * sde_kms_info_reset - reset sde_kms_info structure @@ -368,6 +371,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_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/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c index 685ce3ea968b..4a9997b02155 100644 --- a/drivers/gpu/msm/kgsl_pool.c +++ b/drivers/gpu/msm/kgsl_pool.c @@ -412,6 +412,24 @@ void kgsl_pool_free_page(struct page *page) __free_pages(page, page_order); } +/* + * Return true if the pool of specified page size is supported + * or no pools are supported otherwise return false. + */ +bool kgsl_pool_avaialable(int page_size) +{ + int i; + + if (!kgsl_num_pools) + return true; + + for (i = 0; i < kgsl_num_pools; i++) + if (ilog2(page_size >> PAGE_SHIFT) == kgsl_pools[i].pool_order) + return true; + + return false; +} + static void kgsl_pool_reserve_pages(void) { int i, j; diff --git a/drivers/gpu/msm/kgsl_pool.h b/drivers/gpu/msm/kgsl_pool.h index d55e1ada123b..8091afb1ff11 100644 --- a/drivers/gpu/msm/kgsl_pool.h +++ b/drivers/gpu/msm/kgsl_pool.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 @@ -40,5 +40,6 @@ void kgsl_exit_page_pools(void); int kgsl_pool_alloc_page(int *page_size, struct page **pages, unsigned int pages_len, unsigned int *align); void kgsl_pool_free_page(struct page *p); +bool kgsl_pool_avaialable(int size); #endif /* __KGSL_POOL_H */ diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 27733b068434..d3ba8ca0dc00 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -28,7 +28,6 @@ #include "kgsl_device.h" #include "kgsl_log.h" #include "kgsl_mmu.h" -#include "kgsl_pool.h" /* * The user can set this from debugfs to force failed memory allocations to diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index 7db8ce0413c2..e5da594b77b8 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-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 @@ -363,6 +363,8 @@ static inline void kgsl_free_sgt(struct sg_table *sgt) } } +#include "kgsl_pool.h" + /** * kgsl_get_page_size() - Get supported pagesize * @size: Size of the page @@ -373,11 +375,14 @@ static inline void kgsl_free_sgt(struct sg_table *sgt) #ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS static inline int kgsl_get_page_size(size_t size, unsigned int align) { - if (align >= ilog2(SZ_1M) && size >= SZ_1M) + if (align >= ilog2(SZ_1M) && size >= SZ_1M && + kgsl_pool_avaialable(SZ_1M)) return SZ_1M; - else if (align >= ilog2(SZ_64K) && size >= SZ_64K) + else if (align >= ilog2(SZ_64K) && size >= SZ_64K && + kgsl_pool_avaialable(SZ_64K)) return SZ_64K; - else if (align >= ilog2(SZ_8K) && size >= SZ_8K) + else if (align >= ilog2(SZ_8K) && size >= SZ_8K && + kgsl_pool_avaialable(SZ_8K)) return SZ_8K; else return PAGE_SIZE; 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/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index ce18a512b76a..b30739de79e7 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1728,7 +1728,7 @@ 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; - u64 scm_ret; + u64 scm_ret = 0; if (!arm_smmu_is_static_cb(smmu)) return 0; diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 347a3c17f73a..041c42fb511f 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -514,16 +514,6 @@ void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, __iommu_dma_unmap(iommu_get_domain_for_dev(dev), sg_dma_address(sg)); } -int iommu_dma_supported(struct device *dev, u64 mask) -{ - /* - * 'Special' IOMMUs which don't have the same addressing capability - * as the CPU will have to wait until we have some way to query that - * before they'll be able to use this framework. - */ - return 1; -} - int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return dma_addr == DMA_ERROR_CODE; diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c index bfa7d29701da..c85b3e42c8c8 100644 --- a/drivers/leds/leds-qpnp-wled.c +++ b/drivers/leds/leds-qpnp-wled.c @@ -537,6 +537,11 @@ 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->max_strings; i++) { diff --git a/drivers/media/platform/msm/ais/msm.c b/drivers/media/platform/msm/ais/msm.c index 2a1ec86118c5..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; diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.c b/drivers/media/platform/msm/ais/sensor/msm_sensor.c index a276b03e5294..9655fad5b62b 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.c @@ -10,6 +10,12 @@ * GNU General Public License for more details. */ +#include <media/v4l2-subdev.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> #include "msm_sensor.h" #include "msm_sd.h" #include "msm_cci.h" @@ -21,6 +27,7 @@ #undef CDBG #define CDBG(fmt, args...) pr_debug(fmt, ##args) +#define MAX_SENSOR_V4l2_EVENTS 100 static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl; static struct msm_camera_i2c_fn_t msm_sensor_secure_func_tbl; @@ -405,12 +412,26 @@ static long msm_sensor_subdev_do_ioctl( { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); - + struct v4l2_fh *vfh = file->private_data; switch (cmd) { case VIDIOC_MSM_SENSOR_CFG32: cmd = VIDIOC_MSM_SENSOR_CFG; + case VIDIOC_DQEVENT: { + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) + return -ENOIOCTLCMD; + return v4l2_event_dequeue(vfh, arg, + file->f_flags & O_NONBLOCK); + } + break; + case VIDIOC_SUBSCRIBE_EVENT: + pr_debug("msm_sensor_subdev_do_ioctl:VIDIOC_SUBSCRIBE_EVENT"); + return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg); + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); default: - return msm_sensor_subdev_ioctl(sd, cmd, arg); + pr_debug("msm_sensor.c msm_sensor_subdev_do_ioctl"); + return v4l2_subdev_call(sd, core, ioctl, cmd, arg); } } @@ -1459,8 +1480,108 @@ static int msm_sensor_power(struct v4l2_subdev *sd, int on) return rc; } + +static u32 msm_sensor_evt_mask_to_sensor_event(u32 evt_mask) +{ + u32 evt_id = SENSOR_EVENT_SUBS_MASK_NONE; + + switch (evt_mask) { + case SENSOR_EVENT_MASK_INDEX_SIGNAL_STATUS: + evt_id = SENSOR_EVENT_SIGNAL_STATUS; + break; + default: + evt_id = SENSOR_EVENT_SUBS_MASK_NONE; + break; + } + + return evt_id; +} + +static int msm_sensor_subscribe_event_mask(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub, int evt_mask_index, + u32 evt_id, bool subscribe_flag) +{ + int rc = 0; + + sub->type = evt_id; + + if (subscribe_flag) + rc = v4l2_event_subscribe(fh, sub, + MAX_SENSOR_V4l2_EVENTS, NULL); + else + rc = v4l2_event_unsubscribe(fh, sub); + if (rc != 0) { + pr_err("%s: Subs event_type =0x%x failed\n", + __func__, sub->type); + return rc; + } + return rc; +} + +static int msm_sensor_process_event_subscription(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub, bool subscribe_flag) +{ + int rc = 0, evt_mask_index = 0; + u32 evt_mask = sub->type; + u32 evt_id = 0; + + if (SENSOR_EVENT_SUBS_MASK_NONE == evt_mask) { + pr_err("%s: Subs event_type is None=0x%x\n", + __func__, evt_mask); + return 0; + } + + evt_mask_index = SENSOR_EVENT_MASK_INDEX_SIGNAL_STATUS; + if (evt_mask & (1<<evt_mask_index)) { + evt_id = + msm_sensor_evt_mask_to_sensor_event( + evt_mask_index); + rc = msm_sensor_subscribe_event_mask(fh, sub, + evt_mask_index, evt_id, subscribe_flag); + if (rc != 0) { + pr_err("%s: Subs event index:%d failed\n", + __func__, evt_mask_index); + return rc; + } + } + + return rc; +} + +int msm_sensor_send_event(struct msm_sensor_ctrl_t *s_ctrl, + uint32_t event_type, + struct msm_sensor_event_data *event_data) +{ + struct v4l2_event sensor_event; + + memset(&sensor_event, 0, sizeof(struct v4l2_event)); + sensor_event.id = 0; + sensor_event.type = event_type; + + memcpy(&sensor_event.u.data[0], event_data, + sizeof(struct msm_sensor_event_data)); + v4l2_event_queue(s_ctrl->msm_sd.sd.devnode, &sensor_event); + return 0; +} + +static int msm_sensor_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return msm_sensor_process_event_subscription(fh, sub, true); +} + +static int msm_sensor_unsubscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return msm_sensor_process_event_subscription(fh, sub, false); +} + static struct v4l2_subdev_core_ops msm_sensor_subdev_core_ops = { .ioctl = msm_sensor_subdev_ioctl, + .subscribe_event = msm_sensor_subscribe_event, + .unsubscribe_event = msm_sensor_unsubscribe_event, .s_power = msm_sensor_power, }; diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.h b/drivers/media/platform/msm/ais/sensor/msm_sensor.h index eacd3b05420c..b742d06d3baa 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor.h +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.h @@ -90,10 +90,18 @@ struct msm_sensor_ctrl_t { uint32_t set_mclk_23880000; uint8_t is_csid_tg_mode; uint32_t is_secure; - + /* Interrupt GPIOs */ + struct gpio gpio_array[1]; + /* device status and Flags */ + int irq; struct msm_sensor_init_t s_init; + /* worker to handle interrupts */ + struct delayed_work irq_delayed_work; }; +int msm_sensor_send_event(struct msm_sensor_ctrl_t *s_ctrl, + uint32_t event_type, struct msm_sensor_event_data *event_data); + int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp); int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_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..5e34016d199c 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 = @@ -128,13 +132,20 @@ static int32_t msm_sensor_driver_create_v4l_subdev s_ctrl->sensordata->sensor_name); v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, s_ctrl->pdev); s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; media_entity_init(&s_ctrl->msm_sd.sd.entity, 0, NULL, 0); s_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_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); + msm_sensor_v4l2_subdev_fops.unlocked_ioctl = + msm_sensor_subdev_fops_ioctl; #ifdef CONFIG_COMPAT msm_sensor_v4l2_subdev_fops.compat_ioctl32 = msm_sensor_subdev_fops_ioctl; @@ -624,6 +635,56 @@ static void msm_sensor_fill_sensor_info(struct msm_sensor_ctrl_t *s_ctrl, strlcpy(entity_name, s_ctrl->msm_sd.sd.entity.name, MAX_SENSOR_NAME); } +static irqreturn_t bridge_irq(int irq, void *dev) +{ + struct msm_sensor_ctrl_t *s_ctrl = dev; + + pr_err("msm_sensor_driver: received bridge interrupt:0x%x", + s_ctrl->sensordata->slave_info->sensor_slave_addr); + schedule_delayed_work(&s_ctrl->irq_delayed_work, + msecs_to_jiffies(0)); + return IRQ_HANDLED; +} + +static void bridge_irq_delay_work(struct work_struct *work) +{ + struct msm_sensor_ctrl_t *s_ctrl; + struct msm_camera_i2c_client *sensor_i2c_client; + struct msm_camera_slave_info *slave_info; + const char *sensor_name; + + struct msm_sensor_event_data sensor_event; + + s_ctrl = container_of(work, struct msm_sensor_ctrl_t, + irq_delayed_work.work); + if (!s_ctrl) { + pr_err("%s:%d failed: %pK\n", + __func__, __LINE__, s_ctrl); + goto exit_queue; + } + sensor_i2c_client = s_ctrl->sensor_i2c_client; + slave_info = s_ctrl->sensordata->slave_info; + sensor_name = s_ctrl->sensordata->sensor_name; + + if (!sensor_i2c_client || !slave_info || !sensor_name) { + pr_err("%s:%d failed: %pK %pK %pK\n", + __func__, __LINE__, sensor_i2c_client, slave_info, + sensor_name); + goto exit_queue; + } + + mutex_lock(s_ctrl->msm_sensor_mutex); + /* Fill the sensor event */ + sensor_event.sensor_slave_addr = + slave_info->sensor_slave_addr; + /* Queue the event */ + msm_sensor_send_event(s_ctrl, SENSOR_EVENT_SIGNAL_STATUS, + &sensor_event); + mutex_unlock(s_ctrl->msm_sensor_mutex); +exit_queue: + pr_err("Work IRQ exit"); +} + /* static function definition */ int32_t msm_sensor_driver_probe(void *setting, struct msm_sensor_info_t *probed_info, char *entity_name) @@ -888,11 +949,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,8 +987,66 @@ CSID_TG: msm_sensor_fill_sensor_info(s_ctrl, probed_info, entity_name); + if (slave_info->gpio_intr_config.gpio_num != -1) { + /* Configure INTB interrupt */ + s_ctrl->gpio_array[0].gpio = + slave_info->gpio_intr_config.gpio_num; + s_ctrl->gpio_array[0].flags = 0; + /* Only setup IRQ1 for now... */ + INIT_DELAYED_WORK(&s_ctrl->irq_delayed_work, + bridge_irq_delay_work); + rc = gpio_request_array(&s_ctrl->gpio_array[0], 1); + if (rc < 0) { + pr_err("%s: Failed to request irq_gpio %d", + __func__, rc); + goto cancel_work; + } + + if (gpio_is_valid(s_ctrl->gpio_array[0].gpio)) { + rc |= gpio_direction_input( + s_ctrl->gpio_array[0].gpio); + if (rc) { + pr_err("%s: Failed gpio_direction irq %d", + __func__, rc); + goto cancel_work; + } else { + pr_err("sensor probe IRQ direction succeeded"); + } + } + + s_ctrl->irq = gpio_to_irq(s_ctrl->gpio_array[0].gpio); + if (s_ctrl->irq) { + rc = request_irq(s_ctrl->irq, bridge_irq, + IRQF_ONESHOT | + (slave_info-> + gpio_intr_config.gpio_trigger), + "qcom,camera", s_ctrl); + if (rc) { + pr_err("%s: Failed request_irq %d", + __func__, rc); + goto cancel_work; + } + + } else { + pr_err("%s: Failed gpio_to_irq %d", + __func__, rc); + rc = -EINVAL; + goto cancel_work; + } + + /* Keep irq enabled */ + pr_err("msm_sensor_driver.c irq number = %d", s_ctrl->irq); + } + + /* + Set probe succeeded flag to 1 so that no other camera shall + * probed on this slot + */ + s_ctrl->is_probe_succeed = 1; return rc; +cancel_work: + cancel_delayed_work(&s_ctrl->irq_delayed_work); free_camera_info: kfree(camera_info); free_slave_info: @@ -1118,7 +1232,6 @@ static int32_t msm_sensor_driver_parse(struct msm_sensor_ctrl_t *s_ctrl) /* Store sensor control structure in static database */ g_sctrl[s_ctrl->id] = s_ctrl; CDBG("g_sctrl[%d] %pK", s_ctrl->id, g_sctrl[s_ctrl->id]); - return rc; FREE_DT_DATA: @@ -1171,7 +1284,6 @@ static int32_t msm_sensor_driver_platform_probe(struct platform_device *pdev) /* Fill platform device id*/ pdev->id = s_ctrl->id; - /* Fill device in power info */ s_ctrl->sensordata->power_info.dev = &pdev->dev; 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 4a74e808d86f..3dccb73d9de4 100644 --- a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c +++ b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c @@ -828,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 90edadaed1ef..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 @@ -556,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/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/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index 04ca0fba71e5..194a6583103e 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; @@ -409,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); @@ -743,6 +746,16 @@ static long msm_private_ioctl(struct file *file, void *fh, if (!event_data) return -EINVAL; + switch (cmd) { + case MSM_CAM_V4L2_IOCTL_NOTIFY: + case MSM_CAM_V4L2_IOCTL_CMD_ACK: + case MSM_CAM_V4L2_IOCTL_NOTIFY_DEBUG: + case MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR: + break; + default: + return -ENOTTY; + } + memset(&event, 0, sizeof(struct v4l2_event)); session_id = event_data->session_id; stream_id = event_data->stream_id; @@ -811,11 +824,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; @@ -1000,9 +1015,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(); @@ -1358,6 +1375,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/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c index abf20aef1256..422c7a590a45 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c @@ -2003,8 +2003,10 @@ static void sde_rotator_cancel_request(struct sde_rot_mgr *mgr, sde_rot_mgr_unlock(mgr); for (i = req->count - 1; i >= 0; i--) { entry = req->entries + i; - flush_kthread_worker(&entry->commitq->rot_kw); - flush_kthread_worker(&entry->doneq->rot_kw); + if (entry->commitq) + flush_kthread_worker(&entry->commitq->rot_kw); + if (entry->doneq) + flush_kthread_worker(&entry->doneq->rot_kw); } sde_rot_mgr_lock(mgr); SDEROT_DBG("cancel work done\n"); diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 0f6389370643..78cced2abd47 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -1618,6 +1618,8 @@ static 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; + if (internal_buffers[i].req == NULL) + continue; rc = allocate_and_set_internal_bufs(inst, internal_buffers[i].req, diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c index 5c13b6fef3ec..2be52b10c84b 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -445,7 +445,7 @@ struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, dprintk(VIDC_ERR, "Invalid params, inst: %pK\n", inst); goto exit; } - snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst); + snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%pK", inst); idata = kzalloc(sizeof(struct core_inst_pair), GFP_KERNEL); if (!idata) { 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/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5033107f6e26..9bef77ba29fd 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1286,6 +1286,7 @@ static int _mmc_sd_resume(struct mmc_host *host) if (err) { pr_err("%s: %s: mmc_sd_init_card_failed (%d)\n", mmc_hostname(host), __func__, err); + mmc_power_off(host); goto out; } mmc_card_clr_suspended(host->card); diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 907763ddf234..ab4837128cb2 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -4249,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), @@ -4312,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 0033fea0a800..f9d80b3bbcf9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3041,11 +3041,6 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) * above in sdhci_cmd_irq(). */ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { - if (intmask & SDHCI_INT_DATA_TIMEOUT) { - host->cmd->error = -ETIMEDOUT; - tasklet_schedule(&host->finish_tasklet); - return; - } if (intmask & SDHCI_INT_DATA_END) { /* * Some cards handle busy-end interrupt @@ -3061,6 +3056,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (host->quirks2 & SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD) return; + if (intmask & SDHCI_INT_DATA_TIMEOUT) { + host->cmd->error = -ETIMEDOUT; + tasklet_schedule(&host->finish_tasklet); + return; + } } pr_err("%s: Got data interrupt 0x%08x even " diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig index a8001b41c81c..5b315573387e 100644 --- a/drivers/net/can/spi/Kconfig +++ b/drivers/net/can/spi/Kconfig @@ -12,4 +12,10 @@ config CAN_RH850 depends on HAS_DMA ---help--- Driver for the Renesas RH850 SPI CAN controller. + +config CAN_K61 + tristate "Freescale K61 SPI CAN controllers" + depends on SPI + ---help--- + Driver for the Freescale K61 SPI CAN controllers. endmenu diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile index e84da9b8d5ab..375a6cbfbb67 100644 --- a/drivers/net/can/spi/Makefile +++ b/drivers/net/can/spi/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_CAN_MCP251X) += mcp251x.o obj-$(CONFIG_CAN_RH850) += rh850.o +obj-${CONFIG_CAN_K61} += k61.o diff --git a/drivers/net/can/spi/k61.c b/drivers/net/can/spi/k61.c new file mode 100644 index 000000000000..a567cc653e46 --- /dev/null +++ b/drivers/net/can/spi/k61.c @@ -0,0 +1,927 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/workqueue.h> +#include <linux/spi/spi.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/uaccess.h> + +#define DEBUG_K61 0 +#if DEBUG_K61 == 1 +#define LOGDI(...) dev_info(&priv_data->spidev->dev, __VA_ARGS__) +#define LOGNI(...) netdev_info(netdev, __VA_ARGS__) +#else +#define LOGDI(...) +#define LOGNI(...) +#endif +#define LOGDE(...) dev_err(&priv_data->spidev->dev, __VA_ARGS__) +#define LOGNE(...) netdev_err(netdev, __VA_ARGS__) + +#define MAX_TX_BUFFERS 1 +#define XFER_BUFFER_SIZE 64 +#define K61_CLOCK 120000000 +#define K61_MAX_CHANNELS 1 +#define K61_FW_QUERY_RETRY_COUNT 3 + +struct k61_can { + struct net_device *netdev; + struct spi_device *spidev; + + struct mutex spi_lock; /* SPI device lock */ + + struct workqueue_struct *tx_wq; + char *tx_buf, *rx_buf; + int xfer_length; + atomic_t msg_seq; + + atomic_t netif_queue_stop; + struct completion response_completion; + int reset; + int wait_cmd; + int cmd_result; +}; + +struct k61_netdev_privdata { + struct can_priv can; + struct k61_can *k61_can; +}; + +struct k61_tx_work { + struct work_struct work; + struct sk_buff *skb; + struct net_device *netdev; +}; + +/* Message definitions */ +struct spi_mosi { /* TLV for MOSI line */ + u8 cmd; + u8 len; + u16 seq; + u8 data[]; +} __packed; + +struct spi_miso { /* TLV for MISO line */ + u8 cmd; + u8 len; + u16 seq; /* should match seq field from request, or 0 for unsols */ + u8 data[]; +} __packed; + +#define CMD_GET_FW_VERSION 0x81 +#define CMD_CAN_SEND_FRAME 0x82 +#define CMD_CAN_ADD_FILTER 0x83 +#define CMD_CAN_REMOVE_FILTER 0x84 +#define CMD_CAN_RECEIVE_FRAME 0x85 +#define CMD_CAN_DATA_BUFF_ADD 0x87 +#define CMD_CAN_DATA_BUFF_REMOVE 0x88 +#define CMD_CAN_RELEASE_BUFFER 0x89 +#define CMD_CAN_DATA_BUFF_REMOVE_ALL 0x8A + +#define IOCTL_RELEASE_CAN_BUFFER (SIOCDEVPRIVATE + 0) +#define IOCTL_ENABLE_BUFFERING (SIOCDEVPRIVATE + 1) +#define IOCTL_ADD_FRAME_FILTER (SIOCDEVPRIVATE + 2) +#define IOCTL_REMOVE_FRAME_FILTER (SIOCDEVPRIVATE + 3) +#define IOCTL_DISABLE_BUFFERING (SIOCDEVPRIVATE + 5) +#define IOCTL_DISABLE_ALL_BUFFERING (SIOCDEVPRIVATE + 6) + +struct can_fw_resp { + u8 maj; + u8 min; + u8 ver; +} __packed; + +struct can_write_req { + u32 ts; + u32 mid; + u8 dlc; + u8 data[]; +} __packed; + +struct can_write_resp { + u8 err; +} __packed; + +struct can_receive_frame { + u32 ts; + u32 mid; + u8 dlc; + u8 data[]; +} __packed; + +struct can_add_filter_req { + u8 can_if; + u32 mid; + u32 mask; + u8 type; +} __packed; + +static struct can_bittiming_const k61_bittiming_const = { + .name = "k61", + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 4, + .brp_max = 1023, + .brp_inc = 1, +}; + +struct k61_add_can_buffer { + u8 can_if; + u32 mid; + u32 mask; +} __packed; + +struct k61_delete_can_buffer { + u8 can_if; + u32 mid; + u32 mask; +} __packed; + +static int k61_rx_message(struct k61_can *priv_data); + +static irqreturn_t k61_irq(int irq, void *priv) +{ + struct k61_can *priv_data = priv; + + LOGDI("k61_irq\n"); + k61_rx_message(priv_data); + return IRQ_HANDLED; +} + +static void k61_frame_error(struct k61_can *priv_data, + struct can_receive_frame *frame) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device *netdev; + + netdev = priv_data->netdev; + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) { + LOGDE("skb alloc failed\n"); + return; + } + + cf->can_id |= CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_FORM; + netdev->stats.rx_errors++; + netif_rx(skb); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += cf->can_dlc; +} + +static void k61_receive_frame(struct k61_can *priv_data, + struct can_receive_frame *frame) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct skb_shared_hwtstamps *skt; + struct timeval tv; + static int msec; + struct net_device *netdev; + int i; + + if (frame->dlc > 8) { + LOGDE("can rx frame error\n"); + k61_frame_error(priv_data, frame); + return; + } + + netdev = priv_data->netdev; + skb = alloc_can_skb(netdev, &cf); + if (!skb) { + LOGDE("skb alloc failed\n"); + return; + } + + LOGDI("rcv frame %d %x %d %x %x %x %x %x %x %x %x\n", + frame->ts, frame->mid, frame->dlc, frame->data[0], + frame->data[1], frame->data[2], frame->data[3], frame->data[4], + frame->data[5], frame->data[6], frame->data[7]); + cf->can_id = le32_to_cpu(frame->mid); + cf->can_dlc = get_can_dlc(frame->dlc); + + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = frame->data[i]; + + msec = le32_to_cpu(frame->ts); + tv.tv_sec = msec / 1000; + tv.tv_usec = (msec - tv.tv_sec * 1000) * 1000; + skt = skb_hwtstamps(skb); + skt->hwtstamp = timeval_to_ktime(tv); + LOGDI(" hwtstamp %lld\n", ktime_to_ms(skt->hwtstamp)); + skb->tstamp = timeval_to_ktime(tv); + netif_rx(skb); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += cf->can_dlc; +} + +static void k61_process_response(struct k61_can *priv_data, + struct spi_miso *resp) +{ + int ret = 0; + + LOGDI("<%x %2d [%d]\n", resp->cmd, resp->len, resp->seq); + if (resp->cmd == CMD_CAN_RECEIVE_FRAME) { + struct can_receive_frame *frame = + (struct can_receive_frame *)&resp->data; + k61_receive_frame(priv_data, frame); + } else if (resp->cmd == CMD_GET_FW_VERSION) { + struct can_fw_resp *fw_resp = (struct can_fw_resp *)resp->data; + + dev_info(&priv_data->spidev->dev, "fw %d.%d.%d", + fw_resp->maj, fw_resp->min, fw_resp->ver); + } + + if (resp->cmd == priv_data->wait_cmd) { + priv_data->cmd_result = ret; + complete(&priv_data->response_completion); + } +} + +static void k61_process_rx(struct k61_can *priv_data, char *rx_buf) +{ + struct spi_miso *resp; + int length_processed = 0, actual_length = priv_data->xfer_length; + + while (length_processed < actual_length) { + int length_left = actual_length - length_processed; + int length = 0; /* length of consumed chunk */ + void *data; + + data = rx_buf + length_processed; + resp = (struct spi_miso *)data; + + if (resp->cmd == 0) { + /* special case. ignore cmd==0 */ + length_processed += 1; + continue; + } + + LOGDI("processing. p %d -> l %d (t %d)\n", + length_processed, length_left, priv_data->xfer_length); + length = resp->len + sizeof(*resp); + + if (length <= length_left) { + k61_process_response(priv_data, resp); + length_processed += length; + } else { + /* Incomplete command */ + break; + } + } +} + +static int k61_do_spi_transaction(struct k61_can *priv_data) +{ + struct spi_device *spi; + struct spi_transfer *xfer; + struct spi_message *msg; + int ret; + + spi = priv_data->spidev; + msg = devm_kzalloc(&spi->dev, sizeof(*msg), GFP_KERNEL); + xfer = devm_kzalloc(&spi->dev, sizeof(*xfer), GFP_KERNEL); + if (xfer == 0 || msg == 0) + return -ENOMEM; + spi_message_init(msg); + + spi_message_add_tail(xfer, msg); + xfer->tx_buf = priv_data->tx_buf; + xfer->rx_buf = priv_data->rx_buf; + xfer->len = XFER_BUFFER_SIZE; + xfer->bits_per_word = 16; + + ret = spi_sync(spi, msg); + LOGDI("spi_sync ret %d\n", ret); + + if (ret == 0) { + devm_kfree(&spi->dev, msg); + devm_kfree(&spi->dev, xfer); + k61_process_rx(priv_data, priv_data->rx_buf); + } + return ret; +} + +static int k61_rx_message(struct k61_can *priv_data) +{ + char *tx_buf, *rx_buf; + int ret; + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + ret = k61_do_spi_transaction(priv_data); + mutex_unlock(&priv_data->spi_lock); + + return ret; +} + +static int k61_query_firmware_version(struct k61_can *priv_data) +{ + char *tx_buf, *rx_buf; + int ret; + struct spi_mosi *req; + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + req = (struct spi_mosi *)tx_buf; + req->cmd = CMD_GET_FW_VERSION; + req->len = 0; + req->seq = atomic_inc_return(&priv_data->msg_seq); + + priv_data->wait_cmd = CMD_GET_FW_VERSION; + priv_data->cmd_result = -1; + reinit_completion(&priv_data->response_completion); + + ret = k61_do_spi_transaction(priv_data); + mutex_unlock(&priv_data->spi_lock); + + if (ret == 0) { + wait_for_completion_interruptible_timeout( + &priv_data->response_completion, 0.001 * HZ); + ret = priv_data->cmd_result; + } + + return ret; +} + +static int k61_can_write(struct k61_can *priv_data, struct can_frame *cf) +{ + char *tx_buf, *rx_buf; + int ret, i; + struct spi_mosi *req; + struct can_write_req *req_d; + struct net_device *netdev; + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + req = (struct spi_mosi *)tx_buf; + req->cmd = CMD_CAN_SEND_FRAME; + req->len = sizeof(struct can_write_req) + 8; + req->seq = atomic_inc_return(&priv_data->msg_seq); + + req_d = (struct can_write_req *)req->data; + req_d->mid = cf->can_id; + req_d->dlc = cf->can_dlc; + for (i = 0; i < cf->can_dlc; i++) + req_d->data[i] = cf->data[i]; + + ret = k61_do_spi_transaction(priv_data); + netdev = priv_data->netdev; + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += cf->can_dlc; + mutex_unlock(&priv_data->spi_lock); + + return ret; +} + +static int k61_netdev_open(struct net_device *netdev) +{ + int err; + + LOGNI("Open"); + err = open_candev(netdev); + if (err) + return err; + + netif_start_queue(netdev); + + return 0; +} + +static int k61_netdev_close(struct net_device *netdev) +{ + LOGNI("Close"); + + netif_stop_queue(netdev); + close_candev(netdev); + return 0; +} + +static void k61_send_can_frame(struct work_struct *ws) +{ + struct k61_tx_work *tx_work; + struct can_frame *cf; + struct k61_can *priv_data; + struct net_device *netdev; + struct k61_netdev_privdata *netdev_priv_data; + + tx_work = container_of(ws, struct k61_tx_work, work); + netdev = tx_work->netdev; + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->k61_can; + LOGDI("send_can_frame ws %p\n", ws); + LOGDI("send_can_frame tx %p\n", tx_work); + + cf = (struct can_frame *)tx_work->skb->data; + k61_can_write(priv_data, cf); + + dev_kfree_skb(tx_work->skb); + kfree(tx_work); +} + +static int k61_frame_filter(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + char *tx_buf, *rx_buf; + int ret; + struct spi_mosi *req; + struct can_add_filter_req *add_filter; + struct can_add_filter_req *filter_request; + struct k61_can *priv_data; + struct k61_netdev_privdata *netdev_priv_data; + struct spi_device *spi; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->k61_can; + spi = priv_data->spidev; + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + if (!ifr) + return -EINVAL; + + filter_request = + devm_kzalloc(&spi->dev, sizeof(struct can_add_filter_req), + GFP_KERNEL); + if (!filter_request) + return -ENOMEM; + + if (copy_from_user(filter_request, ifr->ifr_data, + sizeof(struct can_add_filter_req))) { + devm_kfree(&spi->dev, filter_request); + return -EFAULT; + } + + req = (struct spi_mosi *)tx_buf; + if (cmd == IOCTL_ADD_FRAME_FILTER) + req->cmd = CMD_CAN_ADD_FILTER; + else + req->cmd = CMD_CAN_REMOVE_FILTER; + + req->len = sizeof(struct can_add_filter_req); + req->seq = atomic_inc_return(&priv_data->msg_seq); + + add_filter = (struct can_add_filter_req *)req->data; + add_filter->can_if = filter_request->can_if; + add_filter->mid = filter_request->mid; + add_filter->mask = filter_request->mask; + + ret = k61_do_spi_transaction(priv_data); + devm_kfree(&spi->dev, filter_request); + mutex_unlock(&priv_data->spi_lock); + return ret; +} + +static netdev_tx_t k61_netdev_start_xmit( + struct sk_buff *skb, struct net_device *netdev) +{ + struct k61_netdev_privdata *netdev_priv_data = netdev_priv(netdev); + struct k61_can *priv_data = netdev_priv_data->k61_can; + struct k61_tx_work *tx_work; + + LOGNI("netdev_start_xmit"); + if (can_dropped_invalid_skb(netdev, skb)) { + LOGNE("Dropping invalid can frame\n"); + return NETDEV_TX_OK; + } + tx_work = kzalloc(sizeof(*tx_work), GFP_ATOMIC); + if (tx_work == 0) + return NETDEV_TX_OK; + INIT_WORK(&tx_work->work, k61_send_can_frame); + tx_work->netdev = netdev; + tx_work->skb = skb; + queue_work(priv_data->tx_wq, &tx_work->work); + + return NETDEV_TX_OK; +} + +static int k61_send_release_can_buffer_cmd(struct net_device *netdev) +{ + struct k61_can *priv_data; + struct k61_netdev_privdata *netdev_priv_data; + struct spi_device *spi; + char *tx_buf, *rx_buf; + int ret; + struct spi_mosi *req; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->k61_can; + spi = priv_data->spidev; + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + req = (struct spi_mosi *)tx_buf; + req->cmd = CMD_CAN_RELEASE_BUFFER; + req->len = 0; + req->seq = atomic_inc_return(&priv_data->msg_seq); + + ret = k61_do_spi_transaction(priv_data); + mutex_unlock(&priv_data->spi_lock); + return ret; +} + +static int k61_remove_all_buffering(struct net_device *netdev) +{ + char *tx_buf, *rx_buf; + int ret; + struct spi_mosi *req; + struct k61_can *priv_data; + struct k61_netdev_privdata *netdev_priv_data; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->k61_can; + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + req = (struct spi_mosi *)tx_buf; + req->cmd = CMD_CAN_DATA_BUFF_REMOVE_ALL; + req->len = 0; + req->seq = atomic_inc_return(&priv_data->msg_seq); + + priv_data->wait_cmd = req->cmd; + priv_data->cmd_result = -1; + reinit_completion(&priv_data->response_completion); + + ret = k61_do_spi_transaction(priv_data); + mutex_unlock(&priv_data->spi_lock); + + if (ret == 0) { + LOGDI("k61_do_blocking_ioctl ready to wait for response\n"); + /* Flash write may take some time. Hence give 2s as + * wait duration in the worst case. This wait time should + * increase if more number of frame IDs are stored in flash. + */ + ret = wait_for_completion_interruptible_timeout( + &priv_data->response_completion, 2 * HZ); + ret = priv_data->cmd_result; + } + + return ret; +} + +static int k61_convert_ioctl_cmd_to_spi_cmd(int ioctl_cmd) +{ + switch (ioctl_cmd) { + case IOCTL_ENABLE_BUFFERING: + return CMD_CAN_DATA_BUFF_ADD; + case IOCTL_DISABLE_BUFFERING: + return CMD_CAN_DATA_BUFF_REMOVE; + } + return -EINVAL; +} + +static int k61_data_buffering(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + int spi_cmd, ret; + char *tx_buf, *rx_buf; + struct k61_can *priv_data; + struct spi_mosi *req; + struct k61_netdev_privdata *netdev_priv_data; + struct k61_add_can_buffer *enable_buffering; + struct k61_add_can_buffer *add_request; + struct spi_device *spi; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->k61_can; + spi = priv_data->spidev; + + mutex_lock(&priv_data->spi_lock); + spi_cmd = k61_convert_ioctl_cmd_to_spi_cmd(cmd); + if (spi_cmd < 0) { + LOGDE("k61_do_blocking_ioctl wrong command %d\n", cmd); + return spi_cmd; + } + + if (!ifr) + return -EINVAL; + + add_request = devm_kzalloc(&spi->dev, sizeof(struct k61_add_can_buffer), + GFP_KERNEL); + if (!add_request) + return -ENOMEM; + + if (copy_from_user(add_request, ifr->ifr_data, + sizeof(struct k61_add_can_buffer))) { + devm_kfree(&spi->dev, add_request); + return -EFAULT; + } + + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + req = (struct spi_mosi *)tx_buf; + req->cmd = spi_cmd; + req->len = sizeof(struct k61_add_can_buffer); + req->seq = atomic_inc_return(&priv_data->msg_seq); + + enable_buffering = (struct k61_add_can_buffer *)req->data; + enable_buffering->can_if = add_request->can_if; + enable_buffering->mid = add_request->mid; + enable_buffering->mask = add_request->mask; + + priv_data->wait_cmd = spi_cmd; + priv_data->cmd_result = -1; + reinit_completion(&priv_data->response_completion); + + ret = k61_do_spi_transaction(priv_data); + devm_kfree(&spi->dev, add_request); + mutex_unlock(&priv_data->spi_lock); + + if (ret == 0) { + LOGDI("k61_do_blocking_ioctl ready to wait for response\n"); + /* Flash write may take some time. Hence give 400ms as + * wait duration in the worst case. + */ + ret = wait_for_completion_interruptible_timeout( + &priv_data->response_completion, 0.4 * HZ); + ret = priv_data->cmd_result; + } + return ret; +} + +static int k61_netdev_do_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + struct k61_can *priv_data; + struct k61_netdev_privdata *netdev_priv_data; + int ret = -EINVAL; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->k61_can; + LOGDI("k61_netdev_do_ioctl %x\n", cmd); + + switch (cmd) { + case IOCTL_ADD_FRAME_FILTER: + case IOCTL_REMOVE_FRAME_FILTER: + ret = k61_frame_filter(netdev, ifr, cmd); + break; + case IOCTL_ENABLE_BUFFERING: + case IOCTL_DISABLE_BUFFERING: + ret = k61_data_buffering(netdev, ifr, cmd); + break; + case IOCTL_DISABLE_ALL_BUFFERING: + ret = k61_remove_all_buffering(netdev); + break; + case IOCTL_RELEASE_CAN_BUFFER: + ret = k61_send_release_can_buffer_cmd(netdev); + break; + } + return ret; +} + +static const struct net_device_ops k61_netdev_ops = { + .ndo_open = k61_netdev_open, + .ndo_stop = k61_netdev_close, + .ndo_start_xmit = k61_netdev_start_xmit, + .ndo_do_ioctl = k61_netdev_do_ioctl, +}; + +static int k61_create_netdev(struct spi_device *spi, + struct k61_can *priv_data) +{ + struct net_device *netdev; + struct k61_netdev_privdata *netdev_priv_data; + + LOGDI("k61_create_netdev\n"); + netdev = alloc_candev(sizeof(*netdev_priv_data), MAX_TX_BUFFERS); + if (!netdev) { + LOGDE("Couldn't alloc candev\n"); + return -ENOMEM; + } + + netdev_priv_data = netdev_priv(netdev); + netdev_priv_data->k61_can = priv_data; + + priv_data->netdev = netdev; + + netdev->netdev_ops = &k61_netdev_ops; + SET_NETDEV_DEV(netdev, &spi->dev); + netdev_priv_data->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LISTENONLY; + netdev_priv_data->can.bittiming_const = &k61_bittiming_const; + netdev_priv_data->can.clock.freq = K61_CLOCK; + + return 0; +} + +static struct k61_can *k61_create_priv_data(struct spi_device *spi) +{ + struct k61_can *priv_data; + int err; + struct device *dev; + + dev = &spi->dev; + priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) { + dev_err(dev, "Couldn't alloc k61_can\n"); + return 0; + } + spi_set_drvdata(spi, priv_data); + atomic_set(&priv_data->netif_queue_stop, 0); + priv_data->spidev = spi; + + priv_data->tx_wq = alloc_workqueue("k61_tx_wq", 0, 0); + if (!priv_data->tx_wq) { + dev_err(dev, "Couldn't alloc workqueue\n"); + err = -ENOMEM; + goto cleanup_privdata; + } + + priv_data->tx_buf = devm_kzalloc(dev, XFER_BUFFER_SIZE, + GFP_KERNEL); + priv_data->rx_buf = devm_kzalloc(dev, XFER_BUFFER_SIZE, + GFP_KERNEL); + if (!priv_data->tx_buf || !priv_data->rx_buf) { + dev_err(dev, "Couldn't alloc tx or rx buffers\n"); + err = -ENOMEM; + goto cleanup_privdata; + } + priv_data->xfer_length = 0; + + mutex_init(&priv_data->spi_lock); + atomic_set(&priv_data->msg_seq, 0); + init_completion(&priv_data->response_completion); + return priv_data; + +cleanup_privdata: + if (priv_data) { + if (priv_data->tx_wq) + destroy_workqueue(priv_data->tx_wq); + } + return 0; +} + +static int k61_probe(struct spi_device *spi) +{ + int err, retry = 0, query_err = -1; + struct k61_can *priv_data; + struct device *dev; + + dev = &spi->dev; + dev_dbg(dev, "k61_probe"); + + err = spi_setup(spi); + if (err) { + dev_err(dev, "spi_setup failed: %d", err); + return err; + } + + priv_data = k61_create_priv_data(spi); + if (!priv_data) { + dev_err(dev, "Failed to create k61_can priv_data\n"); + err = -ENOMEM; + return err; + } + dev_dbg(dev, "k61_probe created priv_data"); + + priv_data->reset = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); + if (!gpio_is_valid(priv_data->reset)) { + dev_err(&spi->dev, "Missing dt property: reset-gpio\n"); + return -EINVAL; + } + err = gpio_request(priv_data->reset, "k61-reset"); + if (err < 0) { + dev_err(&spi->dev, + "failed to request gpio %d: %d\n", + priv_data->reset, err); + } + + gpio_direction_output(priv_data->reset, 0); + udelay(1); + gpio_direction_output(priv_data->reset, 1); + /* Provide a delay of 300us for the chip to reset. This is part of + * the reset sequence. + */ + usleep_range(300, 301); + + err = k61_create_netdev(spi, priv_data); + if (err) { + dev_err(dev, "Failed to create CAN device: %d", err); + goto cleanup_candev; + } + + err = register_candev(priv_data->netdev); + if (err) { + dev_err(dev, "Failed to register CAN device: %d", err); + goto unregister_candev; + } + + err = request_threaded_irq(spi->irq, NULL, k61_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "k61", priv_data); + if (err) { + dev_err(dev, "Failed to request irq: %d", err); + goto unregister_candev; + } + dev_dbg(dev, "Request irq %d ret %d\n", spi->irq, err); + + while ((query_err != 0) && (retry < K61_FW_QUERY_RETRY_COUNT)) { + query_err = k61_query_firmware_version(priv_data); + retry++; + } + + if (query_err) { + dev_info(dev, "K61 probe failed\n"); + err = -ENODEV; + goto free_irq; + } + return 0; + +free_irq: + free_irq(spi->irq, priv_data); +unregister_candev: + unregister_candev(priv_data->netdev); +cleanup_candev: + if (priv_data) { + if (priv_data->netdev) + free_candev(priv_data->netdev); + if (priv_data->tx_wq) + destroy_workqueue(priv_data->tx_wq); + } + return err; +} + +static int k61_remove(struct spi_device *spi) +{ + struct k61_can *priv_data = spi_get_drvdata(spi); + + LOGDI("k61_remove\n"); + unregister_candev(priv_data->netdev); + free_candev(priv_data->netdev); + destroy_workqueue(priv_data->tx_wq); + return 0; +} + +static const struct of_device_id k61_match_table[] = { + { .compatible = "fsl,k61" }, + { } +}; + +static struct spi_driver k61_driver = { + .driver = { + .name = "k61", + .of_match_table = k61_match_table, + .owner = THIS_MODULE, + }, + .probe = k61_probe, + .remove = k61_remove, +}; +module_spi_driver(k61_driver); + +MODULE_DESCRIPTION("Freescale K61 SPI-CAN module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 9cda1303c9e1..769f89e8d14c 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -1009,11 +1009,17 @@ void ath10k_ce_enable_interrupts(struct ath10k *ar) struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); int ce_id; struct ath10k_ce_pipe *ce_state; + u8 ce_count; + if (QCA_REV_WCN3990(ar)) + ce_count = CE_COUNT; + else /* Skip the last copy engine, CE7 the diagnostic window, as that * uses polling and isn't initialized for interrupts. */ - for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++) { + ce_count = CE_COUNT - 1; + + for (ce_id = 0; ce_id < ce_count; ce_id++) { ce_state = &ar_opaque->ce_states[ce_id]; ath10k_ce_per_engine_handler_adjust(ce_state); } diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ec86c837e60a..4bb14d43e136 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4507,6 +4507,13 @@ static int ath10k_start(struct ieee80211_hw *hw) goto err_core_stop; } + param = ar->wmi.pdev_param->idle_ps_config; + ret = ath10k_wmi_pdev_set_param(ar, param, 1); + if (ret) { + ath10k_warn(ar, "failed to enable idle_ps_config: %d\n", ret); + goto err_core_stop; + } + if (test_bit(WMI_SERVICE_ADAPTIVE_OCS, ar->wmi.svc_map)) { ret = ath10k_wmi_adaptive_qcs(ar, true); if (ret) { diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 94861020af12..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; @@ -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..65f39a4343ff 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -20,7 +20,6 @@ #include <linux/pci.h> #include <linux/rtnetlink.h> #include <linux/power_supply.h> - #include "wil6210.h" #include "wmi.h" #include "txrx.h" @@ -30,7 +29,6 @@ static u32 mem_addr; static u32 dbg_txdesc_index; static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */ -u32 vring_idle_trsh = 16; /* HW fetches up to 16 descriptors at once */ enum dbg_off_type { doff_u32 = 0, @@ -1027,6 +1025,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 +1035,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 +1626,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 +1639,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)); @@ -1761,6 +1772,7 @@ static const struct dbg_off dbg_wil_off[] = { WIL_FIELD(chip_revision, 0444, doff_u8), WIL_FIELD(abft_len, 0644, doff_u8), WIL_FIELD(wakeup_trigger, 0644, doff_u8), + WIL_FIELD(vring_idle_trsh, 0644, doff_u32), {}, }; @@ -1776,8 +1788,6 @@ static const struct dbg_off dbg_statics[] = { {"desc_index", 0644, (ulong)&dbg_txdesc_index, doff_u32}, {"vring_index", 0644, (ulong)&dbg_vring_index, doff_u32}, {"mem_addr", 0644, (ulong)&mem_addr, doff_u32}, - {"vring_idle_trsh", 0644, (ulong)&vring_idle_trsh, - doff_u32}, {"led_polarity", 0644, (ulong)&led_polarity, doff_u8}, {}, }; @@ -1804,6 +1814,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..09da8e769394 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,9 @@ 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; + wil->vring_idle_trsh = 16; return 0; @@ -1036,6 +1040,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0); wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0); + if (wil->fw_calib_result > 0) { + __le32 val = cpu_to_le32(wil->fw_calib_result | + (CALIB_RESULT_SIGNATURE << 8)); + wil_w(wil, RGF_USER_FW_CALIB_RESULT, (u32 __force)val); + } + wil_release_cpu(wil); } 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/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 8f1e79b425cf..8fe2239603d1 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1666,7 +1666,7 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring, /* performance monitoring */ used = wil_vring_used_tx(vring); - if (wil_val_in_range(vring_idle_trsh, + if (wil_val_in_range(wil->vring_idle_trsh, used, used + descs_used)) { txdata->idle += get_cycles() - txdata->last_idle; wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n", @@ -1813,7 +1813,7 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, /* performance monitoring */ used = wil_vring_used_tx(vring); - if (wil_val_in_range(vring_idle_trsh, + if (wil_val_in_range(wil->vring_idle_trsh, used, used + nr_frags + 1)) { txdata->idle += get_cycles() - txdata->last_idle; wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n", @@ -2175,7 +2175,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) /* performance monitoring */ used_new = wil_vring_used_tx(vring); - if (wil_val_in_range(vring_idle_trsh, + if (wil_val_in_range(wil->vring_idle_trsh, used_new, used_before_complete)) { wil_dbg_txrx(wil, "Ring[%2d] idle %d -> %d\n", ringid, used_before_complete, used_new); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 9525f521d215..c9ab282c161a 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -31,7 +31,6 @@ extern bool no_fw_recovery; extern unsigned int mtu_max; extern unsigned short rx_ring_overflow_thrsh; extern int agg_wsize; -extern u32 vring_idle_trsh; extern bool rx_align_2; extern bool rx_large_buf; extern bool debug_fw; @@ -91,6 +90,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, @@ -170,6 +174,10 @@ struct RGF_ICR { #define RGF_USER_USER_SCRATCH_PAD (0x8802bc) #define RGF_USER_BL (0x880A3C) /* Boot Loader */ #define RGF_USER_FW_REV_ID (0x880a8c) /* chip revision */ +#define RGF_USER_FW_CALIB_RESULT (0x880a90) /* b0-7:result + * b8-15:signature + */ + #define CALIB_RESULT_SIGNATURE (0x11) #define RGF_USER_CLKS_CTL_0 (0x880abc) #define BIT_USER_CLKS_CAR_AHB_SW_SEL BIT(1) /* ref clk/PLL */ #define BIT_USER_CLKS_RST_PWGD BIT(11) /* reset on "power good" */ @@ -692,6 +700,7 @@ struct wil6210_priv { u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ struct wil_sta_info sta[WIL6210_MAX_CID]; int bcast_vring; + u32 vring_idle_trsh; /* HW fetches up to 16 descriptors at once */ bool use_extended_dma_addr; /* indicates whether we are using 48 bits */ /* scan */ struct cfg80211_scan_request *scan_request; @@ -731,6 +740,8 @@ struct wil6210_priv { bool tt_data_set; struct wmi_tt_data tt_data; + int fw_calib_result; + #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP struct notifier_block pm_notify; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index fba0d6a79ae2..e421fdad81e2 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -345,6 +345,11 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) strlcpy(wdev->wiphy->fw_version, wil->fw_version, sizeof(wdev->wiphy->fw_version)); + if (len > offsetof(struct wmi_ready_event, rfc_read_calib_result)) { + wil_dbg_wmi(wil, "rfc calibration result %d\n", + evt->rfc_read_calib_result); + wil->fw_calib_result = evt->rfc_read_calib_result; + } wil_set_recovery_state(wil, fw_recovery_idle); set_bit(wil_status_fwready, wil->status); /* let the reset sequence continue */ @@ -382,12 +387,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..1b426d7ef81f 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, }; @@ -1299,6 +1300,8 @@ struct wmi_ready_event { /* enum wmi_phy_capability */ u8 phy_capability; u8 numof_additional_mids; + u8 rfc_read_calib_result; + u8 reserved[3]; } __packed; /* WMI_NOTIFY_REQ_DONE_EVENTID */ @@ -1306,7 +1309,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 +1606,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/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_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/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h index a3081433cc2b..9b56eb0c02fb 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h @@ -77,7 +77,7 @@ #define QMI_WLFW_FUNCTION_NAME_LEN_V01 128 #define QMI_WLFW_MAX_NUM_CE_V01 12 #define QMI_WLFW_MAX_TIMESTAMP_LEN_V01 32 -#define QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01 512 +#define QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01 6144 #define QMI_WLFW_MAX_BUILD_ID_LEN_V01 128 #define QMI_WLFW_MAX_STR_LEN_V01 16 #define QMI_WLFW_MAX_NUM_SHADOW_REG_V01 24 @@ -483,7 +483,7 @@ struct wlfw_athdiag_read_resp_msg_v01 { u8 data[QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01]; }; -#define WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN 524 +#define WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN 6156 extern struct elem_info wlfw_athdiag_read_resp_msg_v01_ei[]; struct wlfw_athdiag_write_req_msg_v01 { @@ -493,7 +493,7 @@ struct wlfw_athdiag_write_req_msg_v01 { u8 data[QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01]; }; -#define WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN 531 +#define WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN 6163 extern struct elem_info wlfw_athdiag_write_req_msg_v01_ei[]; struct wlfw_athdiag_write_resp_msg_v01 { diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index cb95f6e98956..4275e3d26157 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -83,6 +83,10 @@ const char *ipa_event_name[] = { __stringify(IPA_QUOTA_REACH), __stringify(IPA_SSR_BEFORE_SHUTDOWN), __stringify(IPA_SSR_AFTER_POWERUP), + __stringify(ADD_VLAN_IFACE), + __stringify(DEL_VLAN_IFACE), + __stringify(ADD_L2TP_VLAN_MAPPING), + __stringify(DEL_L2TP_VLAN_MAPPING) }; const char *ipa_hdr_l2_type_name[] = { diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 0531919487d7..293a60a60881 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -53,7 +53,7 @@ int __ipa_generate_rt_hw_rule_v2(enum ipa_ip_type ip, int pipe_idx; if (buf == NULL) { - memset(tmp, 0, IPA_RT_FLT_HW_RULE_BUF_SIZE); + memset(tmp, 0, (IPA_RT_FLT_HW_RULE_BUF_SIZE/4)); buf = (u8 *)tmp; } @@ -75,8 +75,15 @@ int __ipa_generate_rt_hw_rule_v2(enum ipa_ip_type ip, rule_hdr->u.hdr.pipe_dest_idx = pipe_idx; rule_hdr->u.hdr.system = !ipa_ctx->hdr_tbl_lcl; if (entry->hdr) { - rule_hdr->u.hdr.hdr_offset = - entry->hdr->offset_entry->offset >> 2; + if (entry->hdr->cookie == IPA_HDR_COOKIE) { + rule_hdr->u.hdr.hdr_offset = + entry->hdr->offset_entry->offset >> 2; + } else { + IPAERR("Entry hdr deleted by user = %d cookie = %u\n", + entry->hdr->user_deleted, entry->hdr->cookie); + WARN_ON(1); + rule_hdr->u.hdr.hdr_offset = 0; + } } else { rule_hdr->u.hdr.hdr_offset = 0; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 52f99c830b47..4cba8a7573bb 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -143,6 +143,9 @@ #define IPA_IOC_ALLOC_NAT_MEM32 _IOWR(IPA_IOC_MAGIC, \ IPA_IOCTL_ALLOC_NAT_MEM, \ compat_uptr_t) +#define IPA_IOC_ALLOC_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_ALLOC_NAT_TABLE, \ + compat_uptr_t) #define IPA_IOC_V4_INIT_NAT32 _IOWR(IPA_IOC_MAGIC, \ IPA_IOCTL_V4_INIT_NAT, \ compat_uptr_t) @@ -152,6 +155,9 @@ #define IPA_IOC_V4_DEL_NAT32 _IOWR(IPA_IOC_MAGIC, \ IPA_IOCTL_V4_DEL_NAT, \ compat_uptr_t) +#define IPA_IOC_DEL_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_DEL_NAT_TABLE, \ + compat_uptr_t) #define IPA_IOC_GET_NAT_OFFSET32 _IOWR(IPA_IOC_MAGIC, \ IPA_IOCTL_GET_NAT_OFFSET, \ compat_uptr_t) @@ -207,6 +213,18 @@ struct ipa3_ioc_nat_alloc_mem32 { compat_size_t size; compat_off_t offset; }; + +/** +* struct ipa_ioc_nat_ipv6ct_table_alloc32 - table memory allocation +* properties +* @size: input parameter, size of table in bytes +* @offset: output parameter, offset into page in case of system memory +*/ +struct ipa_ioc_nat_ipv6ct_table_alloc32 { + compat_size_t size; + compat_off_t offset; +}; + #endif #define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311 @@ -611,6 +629,90 @@ static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type) return 0; } +static void ipa3_vlan_l2tp_msg_free_cb(void *buff, u32 len, u32 type) +{ + if (!buff) { + IPAERR("Null buffer\n"); + return; + } + + if (type != ADD_VLAN_IFACE && + type != DEL_VLAN_IFACE && + type != ADD_L2TP_VLAN_MAPPING && + type != DEL_L2TP_VLAN_MAPPING) { + IPAERR("Wrong type given. buff %pK type %d\n", buff, type); + return; + } + + kfree(buff); +} + +static int ipa3_send_vlan_l2tp_msg(unsigned long usr_param, uint8_t msg_type) +{ + int retval; + struct ipa_ioc_vlan_iface_info *vlan_info; + struct ipa_ioc_l2tp_vlan_mapping_info *mapping_info; + struct ipa_msg_meta msg_meta; + + if (msg_type == ADD_VLAN_IFACE || + msg_type == DEL_VLAN_IFACE) { + vlan_info = kzalloc(sizeof(struct ipa_ioc_vlan_iface_info), + GFP_KERNEL); + if (!vlan_info) { + IPAERR("no memory\n"); + return -ENOMEM; + } + + if (copy_from_user((u8 *)vlan_info, (void __user *)usr_param, + sizeof(struct ipa_ioc_vlan_iface_info))) { + kfree(vlan_info); + return -EFAULT; + } + + memset(&msg_meta, 0, sizeof(msg_meta)); + msg_meta.msg_type = msg_type; + msg_meta.msg_len = sizeof(struct ipa_ioc_vlan_iface_info); + retval = ipa3_send_msg(&msg_meta, vlan_info, + ipa3_vlan_l2tp_msg_free_cb); + if (retval) { + IPAERR("ipa3_send_msg failed: %d\n", retval); + kfree(vlan_info); + return retval; + } + } else if (msg_type == ADD_L2TP_VLAN_MAPPING || + msg_type == DEL_L2TP_VLAN_MAPPING) { + mapping_info = kzalloc(sizeof(struct + ipa_ioc_l2tp_vlan_mapping_info), GFP_KERNEL); + if (!mapping_info) { + IPAERR("no memory\n"); + return -ENOMEM; + } + + if (copy_from_user((u8 *)mapping_info, + (void __user *)usr_param, + sizeof(struct ipa_ioc_l2tp_vlan_mapping_info))) { + kfree(mapping_info); + return -EFAULT; + } + + memset(&msg_meta, 0, sizeof(msg_meta)); + msg_meta.msg_type = msg_type; + msg_meta.msg_len = sizeof(struct + ipa_ioc_l2tp_vlan_mapping_info); + retval = ipa3_send_msg(&msg_meta, mapping_info, + ipa3_vlan_l2tp_msg_free_cb); + if (retval) { + IPAERR("ipa3_send_msg failed: %d\n", retval); + kfree(mapping_info); + return retval; + } + } else { + IPAERR("Unexpected event\n"); + return -EFAULT; + } + + return 0; +} static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -619,8 +721,10 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) u8 header[128] = { 0 }; u8 *param = NULL; struct ipa_ioc_nat_alloc_mem nat_mem; + struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc; struct ipa_ioc_v4_nat_init nat_init; struct ipa_ioc_v4_nat_del nat_del; + struct ipa_ioc_nat_ipv6ct_table_del table_del; struct ipa_ioc_rm_dependency rm_depend; size_t sz; int pre_entry; @@ -659,6 +763,26 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) break; } break; + + case IPA_IOC_ALLOC_NAT_TABLE: + if (copy_from_user(&table_alloc, (const void __user *)arg, + sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc))) { + retval = -EFAULT; + break; + } + + if (ipa3_allocate_nat_table(&table_alloc)) { + retval = -EFAULT; + break; + } + if (table_alloc.offset && + copy_to_user((void __user *)arg, &table_alloc, sizeof( + struct ipa_ioc_nat_ipv6ct_table_alloc))) { + retval = -EFAULT; + break; + } + break; + case IPA_IOC_V4_INIT_NAT: if (copy_from_user((u8 *)&nat_init, (u8 *)arg, sizeof(struct ipa_ioc_v4_nat_init))) { @@ -719,6 +843,18 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; + case IPA_IOC_DEL_NAT_TABLE: + if (copy_from_user(&table_del, (const void __user *)arg, + sizeof(struct ipa_ioc_nat_ipv6ct_table_del))) { + retval = -EFAULT; + break; + } + if (ipa3_del_nat_table(&table_del)) { + retval = -EFAULT; + break; + } + break; + case IPA_IOC_ADD_HDR: if (copy_from_user(header, (u8 *)arg, sizeof(struct ipa_ioc_add_hdr))) { @@ -1582,6 +1718,34 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; + case IPA_IOC_ADD_VLAN_IFACE: + if (ipa3_send_vlan_l2tp_msg(arg, ADD_VLAN_IFACE)) { + retval = -EFAULT; + break; + } + break; + + case IPA_IOC_DEL_VLAN_IFACE: + if (ipa3_send_vlan_l2tp_msg(arg, DEL_VLAN_IFACE)) { + retval = -EFAULT; + break; + } + break; + + case IPA_IOC_ADD_L2TP_VLAN_MAPPING: + if (ipa3_send_vlan_l2tp_msg(arg, ADD_L2TP_VLAN_MAPPING)) { + retval = -EFAULT; + break; + } + break; + + case IPA_IOC_DEL_L2TP_VLAN_MAPPING: + if (ipa3_send_vlan_l2tp_msg(arg, DEL_L2TP_VLAN_MAPPING)) { + retval = -EFAULT; + break; + } + break; + default: /* redundant, as cmd was checked against MAXNR */ IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return -ENOTTY; @@ -2997,6 +3161,34 @@ static void ipa3_teardown_apps_pipes(void) } #ifdef CONFIG_COMPAT +static long compat_ipa3_nat_ipv6ct_alloc_table(unsigned long arg, + int (alloc_func)(struct ipa_ioc_nat_ipv6ct_table_alloc *)) +{ + long retval; + struct ipa_ioc_nat_ipv6ct_table_alloc32 table_alloc32; + struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc; + + retval = copy_from_user(&table_alloc32, (const void __user *)arg, + sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc32)); + if (retval) + return retval; + + table_alloc.size = (size_t)table_alloc32.size; + table_alloc.offset = (off_t)table_alloc32.offset; + + retval = alloc_func(&table_alloc); + if (retval) + return retval; + + if (table_alloc.offset) { + table_alloc32.offset = (compat_off_t)table_alloc.offset; + retval = copy_to_user((void __user *)arg, &table_alloc32, + sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc32)); + } + + return retval; +} + long compat_ipa3_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int retval = 0; @@ -3068,6 +3260,9 @@ long compat_ipa3_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } ret: return retval; + case IPA_IOC_ALLOC_NAT_TABLE32: + return compat_ipa3_nat_ipv6ct_alloc_table(arg, + ipa3_allocate_nat_table); case IPA_IOC_V4_INIT_NAT32: cmd = IPA_IOC_V4_INIT_NAT; break; @@ -3077,6 +3272,9 @@ ret: case IPA_IOC_V4_DEL_NAT32: cmd = IPA_IOC_V4_DEL_NAT; break; + case IPA_IOC_DEL_NAT_TABLE32: + cmd = IPA_IOC_DEL_NAT_TABLE; + break; case IPA_IOC_GET_NAT_OFFSET32: cmd = IPA_IOC_GET_NAT_OFFSET; break; @@ -4075,12 +4273,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) { @@ -4096,7 +4294,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; } @@ -4111,15 +4309,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)) { @@ -4127,7 +4325,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; } @@ -4157,25 +4355,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; } @@ -4619,6 +4819,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); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index fbf84ab7d2d4..c7ab616cb5b8 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -64,6 +64,10 @@ const char *ipa3_event_name[] = { __stringify(IPA_QUOTA_REACH), __stringify(IPA_SSR_BEFORE_SHUTDOWN), __stringify(IPA_SSR_AFTER_POWERUP), + __stringify(ADD_VLAN_IFACE), + __stringify(DEL_VLAN_IFACE), + __stringify(ADD_L2TP_VLAN_MAPPING), + __stringify(DEL_L2TP_VLAN_MAPPING) }; const char *ipa3_hdr_l2_type_name[] = { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 4278dc45aad2..7da78457f1b1 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -38,7 +38,6 @@ #include "ipa_uc_offload_i.h" #define DRV_NAME "ipa" -#define NAT_DEV_NAME "ipaNatTable" #define IPA_COOKIE 0x57831603 #define IPA_RT_RULE_COOKIE 0x57831604 #define IPA_RT_TBL_COOKIE 0x57831605 @@ -1231,6 +1230,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; @@ -1640,12 +1640,15 @@ int ipa3_reset_flt(enum ipa_ip_type ip); * NAT */ int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem); +int ipa3_allocate_nat_table( + struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc); int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init); int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma); int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del); +int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del); /* * Messaging 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_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c index 0256ff89ae24..a78a0a608cb4 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c @@ -34,7 +34,6 @@ enum nat_table_type { #define NAT_TABLE_ENTRY_SIZE_BYTE 32 #define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4 - static int ipa3_nat_vma_fault_remap( struct vm_area_struct *vma, struct vm_fault *vmf) { @@ -167,7 +166,7 @@ int ipa3_create_nat_device(void) IPADBG("\n"); mutex_lock(&nat_ctx->lock); - nat_ctx->class = class_create(THIS_MODULE, NAT_DEV_NAME); + nat_ctx->class = class_create(THIS_MODULE, IPA_NAT_DEV_NAME); if (IS_ERR(nat_ctx->class)) { IPAERR("unable to create the class\n"); result = -ENODEV; @@ -176,7 +175,7 @@ int ipa3_create_nat_device(void) result = alloc_chrdev_region(&nat_ctx->dev_num, 0, 1, - NAT_DEV_NAME); + IPA_NAT_DEV_NAME); if (result) { IPAERR("alloc_chrdev_region err.\n"); result = -ENODEV; @@ -185,7 +184,7 @@ int ipa3_create_nat_device(void) nat_ctx->dev = device_create(nat_ctx->class, NULL, nat_ctx->dev_num, nat_ctx, - "%s", NAT_DEV_NAME); + "%s", IPA_NAT_DEV_NAME); if (IS_ERR(nat_ctx->dev)) { IPAERR("device_create err:%ld\n", PTR_ERR(nat_ctx->dev)); @@ -253,9 +252,10 @@ int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem) IPADBG("passed memory size %zu\n", mem->size); mutex_lock(&nat_ctx->lock); - if (strcmp(mem->dev_name, NAT_DEV_NAME)) { + if (strcmp(IPA_NAT_DEV_NAME, mem->dev_name)) { IPAERR_RL("Nat device name mismatch\n"); - IPAERR_RL("Expect: %s Recv: %s\n", NAT_DEV_NAME, mem->dev_name); + IPAERR_RL("Expect: %s Recv: %s\n", + IPA_NAT_DEV_NAME, mem->dev_name); result = -EPERM; goto bail; } @@ -306,6 +306,34 @@ bail: return result; } +/** +* ipa3_allocate_nat_table() - Allocates memory for the NAT table +* @table_alloc: [in/out] memory parameters +* +* Called by NAT client to allocate memory for the table entries. +* Based on the request size either shared or system memory will be used. +* +* Returns: 0 on success, negative on failure +*/ +int ipa3_allocate_nat_table(struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc) +{ + int result; + struct ipa_ioc_nat_alloc_mem tmp; + + strlcpy(tmp.dev_name, IPA_NAT_DEV_NAME, IPA_RESOURCE_NAME_MAX); + tmp.size = table_alloc->size; + tmp.offset = 0; + + result = ipa3_allocate_nat_device(&tmp); + if (result) + goto bail; + + table_alloc->offset = tmp.offset; + +bail: + return result; +} + /* IOCTL function handlers */ /** * ipa3_nat_init_cmd() - Post IP_V4_NAT_INIT command to IPA HW @@ -833,3 +861,22 @@ destroy_regwrt_imm_cmd: bail: return result; } + +/** +* ipa3_del_nat_table() - Delete the NAT table +* @del: [in] delete table parameters +* +* Called by NAT client to delete the table +* +* Returns: 0 on success, negative on failure +*/ +int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del) +{ + struct ipa_ioc_v4_nat_del tmp; + + tmp.table_index = del->table_index; + tmp.public_ip_addr = ipa3_ctx->nat_mem.public_ip_addr; + + return ipa3_nat_del_cmd(&tmp); +} + diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 8e790c89ed13..ff57e3bd48f0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -72,11 +72,18 @@ static int ipa_generate_rt_hw_rule(enum ipa_ip_type ip, if (entry->proc_ctx || (entry->hdr && entry->hdr->is_hdr_proc_ctx)) { struct ipa3_hdr_proc_ctx_entry *proc_ctx; proc_ctx = (entry->proc_ctx) ? : entry->hdr->proc_ctx; - gen_params.hdr_lcl = ipa3_ctx->hdr_proc_ctx_tbl_lcl; - gen_params.hdr_type = IPAHAL_RT_RULE_HDR_PROC_CTX; - gen_params.hdr_ofst = proc_ctx->offset_entry->offset + - ipa3_ctx->hdr_proc_ctx_tbl.start_offset; - } else if (entry->hdr) { + if ((proc_ctx == NULL) || + (proc_ctx->cookie != IPA_PROC_HDR_COOKIE)) { + gen_params.hdr_type = IPAHAL_RT_RULE_HDR_NONE; + gen_params.hdr_ofst = 0; + } else { + gen_params.hdr_lcl = ipa3_ctx->hdr_proc_ctx_tbl_lcl; + gen_params.hdr_type = IPAHAL_RT_RULE_HDR_PROC_CTX; + gen_params.hdr_ofst = proc_ctx->offset_entry->offset + + ipa3_ctx->hdr_proc_ctx_tbl.start_offset; + } + } else if ((entry->hdr != NULL) && + (entry->hdr->cookie == IPA_HDR_COOKIE)) { gen_params.hdr_lcl = ipa3_ctx->hdr_tbl_lcl; gen_params.hdr_type = IPAHAL_RT_RULE_HDR_RAW; gen_params.hdr_ofst = entry->hdr->offset_entry->offset; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 545c2b599a6f..4979f62b928f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -2983,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); } /** @@ -2996,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/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/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index a45a51490817..2e9ff2afcba2 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -247,6 +247,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(low_power), POWER_SUPPLY_ATTR(temp_cool), POWER_SUPPLY_ATTR(temp_warm), + POWER_SUPPLY_ATTR(temp_cold), + POWER_SUPPLY_ATTR(temp_hot), POWER_SUPPLY_ATTR(system_temp_level), POWER_SUPPLY_ATTR(resistance), POWER_SUPPLY_ATTR(resistance_capacitive), diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 88dcdd8fd7be..16441d6e09e4 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -104,7 +104,7 @@ enum sram_access_flags { }; /* JEITA */ -enum { +enum jeita_levels { JEITA_COLD = 0, JEITA_COOL, JEITA_WARM, @@ -239,6 +239,7 @@ enum ttf_mode { struct fg_dt_props { bool force_load_profile; bool hold_soc_while_full; + bool linearize_soc; bool auto_recharge_soc; int cutoff_volt_mv; int empty_volt_mv; diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 361efd4fbbbd..4788053115e1 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -576,6 +576,41 @@ static int fg_get_charge_counter(struct fg_chip *chip, int *val) return 0; } +static int fg_get_jeita_threshold(struct fg_chip *chip, + enum jeita_levels level, int *temp_decidegC) +{ + int rc; + u8 val; + u16 reg; + + switch (level) { + case JEITA_COLD: + reg = BATT_INFO_JEITA_TOO_COLD(chip); + break; + case JEITA_COOL: + reg = BATT_INFO_JEITA_COLD(chip); + break; + case JEITA_WARM: + reg = BATT_INFO_JEITA_HOT(chip); + break; + case JEITA_HOT: + reg = BATT_INFO_JEITA_TOO_HOT(chip); + break; + default: + return -EINVAL; + } + + rc = fg_read(chip, reg, &val, 1); + if (rc < 0) { + pr_err("Error in reading jeita level %d, rc=%d\n", level, rc); + return rc; + } + + /* Resolution is 0.5C. Base is -30C. */ + *temp_decidegC = (((5 * val) / 10) - 30) * 10; + return 0; +} + #define BATT_TEMP_NUMR 1 #define BATT_TEMP_DENR 1 static int fg_get_battery_temp(struct fg_chip *chip, int *val) @@ -838,7 +873,7 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val) if (rc < 0) return rc; - if (chip->delta_soc > 0) + if (chip->dt.linearize_soc && chip->delta_soc > 0) *val = chip->maint_soc; else *val = msoc; @@ -978,12 +1013,6 @@ static int fg_get_batt_profile(struct fg_chip *chip) return 0; } -static inline void get_temp_setpoint(int threshold, u8 *val) -{ - /* Resolution is 0.5C. Base is -30C. */ - *val = DIV_ROUND_CLOSEST((threshold + 30) * 10, 5); -} - static inline void get_batt_temp_delta(int delta, u8 *val) { switch (delta) { @@ -1658,12 +1687,38 @@ static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv) return 0; } +static int fg_configure_full_soc(struct fg_chip *chip, int bsoc) +{ + int rc; + u8 full_soc[2] = {0xFF, 0xFF}; + + /* + * Once SOC masking condition is cleared, FULL_SOC and MONOTONIC_SOC + * needs to be updated to reflect the same. Write battery SOC to + * FULL_SOC and write a full value to MONOTONIC_SOC. + */ + rc = fg_sram_write(chip, FULL_SOC_WORD, FULL_SOC_OFFSET, + (u8 *)&bsoc, 2, FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("failed to write full_soc rc=%d\n", rc); + return rc; + } + + rc = fg_sram_write(chip, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET, + full_soc, 2, FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("failed to write monotonic_soc rc=%d\n", rc); + return rc; + } + + return 0; +} + #define AUTO_RECHG_VOLT_LOW_LIMIT_MV 3700 static int fg_charge_full_update(struct fg_chip *chip) { union power_supply_propval prop = {0, }; int rc, msoc, bsoc, recharge_soc, msoc_raw; - u8 full_soc[2] = {0xFF, 0xFF}; if (!chip->dt.hold_soc_while_full) return 0; @@ -1722,24 +1777,24 @@ static int fg_charge_full_update(struct fg_chip *chip) fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n", msoc); } - } else if (msoc_raw < recharge_soc && chip->charge_full) { - chip->delta_soc = FULL_CAPACITY - msoc; + } else if (msoc_raw <= recharge_soc && chip->charge_full) { + if (chip->dt.linearize_soc) { + chip->delta_soc = FULL_CAPACITY - msoc; - /* - * We're spreading out the delta SOC over every 10% change - * in monotonic SOC. We cannot spread more than 9% in the - * range of 0-100 skipping the first 10%. - */ - if (chip->delta_soc > 9) { - chip->delta_soc = 0; - chip->maint_soc = 0; - } else { - chip->maint_soc = FULL_CAPACITY; - chip->last_msoc = msoc; + /* + * We're spreading out the delta SOC over every 10% + * change in monotonic SOC. We cannot spread more than + * 9% in the range of 0-100 skipping the first 10%. + */ + if (chip->delta_soc > 9) { + chip->delta_soc = 0; + chip->maint_soc = 0; + } else { + chip->maint_soc = FULL_CAPACITY; + chip->last_msoc = msoc; + } } - chip->charge_full = false; - /* * Raise the recharge voltage so that VBAT_LT_RECHG signal * will be asserted soon as battery SOC had dropped below @@ -1752,35 +1807,23 @@ static int fg_charge_full_update(struct fg_chip *chip) rc); goto out; } - 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; - } - if (!chip->charge_full) - goto out; + /* + * If charge_done is still set, wait for recharging or + * discharging to happen. + */ + if (chip->charge_done) + goto out; - /* - * During JEITA conditions, charge_full can happen early. FULL_SOC - * and MONOTONIC_SOC needs to be updated to reflect the same. Write - * battery SOC to FULL_SOC and write a full value to MONOTONIC_SOC. - */ - rc = fg_sram_write(chip, FULL_SOC_WORD, FULL_SOC_OFFSET, (u8 *)&bsoc, 2, - FG_IMA_ATOMIC); - if (rc < 0) { - pr_err("failed to write full_soc rc=%d\n", rc); - goto out; - } + rc = fg_configure_full_soc(chip, bsoc); + if (rc < 0) + goto out; - rc = fg_sram_write(chip, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET, - full_soc, 2, FG_IMA_ATOMIC); - if (rc < 0) { - pr_err("failed to write monotonic_soc rc=%d\n", rc); - goto out; + chip->charge_full = false; + 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); } - fg_dbg(chip, FG_STATUS, "Set charge_full to true @ soc %d\n", msoc); out: mutex_unlock(&chip->charge_full_lock); return rc; @@ -1863,6 +1906,44 @@ static int fg_rconn_config(struct fg_chip *chip) return 0; } +static int fg_set_jeita_threshold(struct fg_chip *chip, + enum jeita_levels level, int temp_decidegC) +{ + int rc; + u8 val; + u16 reg; + + if (temp_decidegC < -300 || temp_decidegC > 970) + return -EINVAL; + + /* Resolution is 0.5C. Base is -30C. */ + val = DIV_ROUND_CLOSEST(((temp_decidegC / 10) + 30) * 10, 5); + switch (level) { + case JEITA_COLD: + reg = BATT_INFO_JEITA_TOO_COLD(chip); + break; + case JEITA_COOL: + reg = BATT_INFO_JEITA_COLD(chip); + break; + case JEITA_WARM: + reg = BATT_INFO_JEITA_HOT(chip); + break; + case JEITA_HOT: + reg = BATT_INFO_JEITA_TOO_HOT(chip); + break; + default: + return -EINVAL; + } + + rc = fg_write(chip, reg, &val, 1); + if (rc < 0) { + pr_err("Error in setting jeita level %d, rc=%d\n", level, rc); + return rc; + } + + return 0; +} + static int fg_set_constant_chg_voltage(struct fg_chip *chip, int volt_uv) { u8 buf[2]; @@ -3137,6 +3218,9 @@ static int fg_update_maint_soc(struct fg_chip *chip) { int rc = 0, msoc; + if (!chip->dt.linearize_soc) + return 0; + mutex_lock(&chip->charge_full_lock); if (chip->delta_soc <= 0) goto out; @@ -3365,6 +3449,9 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: rc = fg_get_prop_capacity(chip, &pval->intval); break; + case POWER_SUPPLY_PROP_CAPACITY_RAW: + rc = fg_get_msoc_raw(chip, &pval->intval); + break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: if (chip->battery_missing) pval->intval = 3700000; @@ -3377,6 +3464,34 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TEMP: rc = fg_get_battery_temp(chip, &pval->intval); break; + case POWER_SUPPLY_PROP_COLD_TEMP: + rc = fg_get_jeita_threshold(chip, JEITA_COLD, &pval->intval); + if (rc < 0) { + pr_err("Error in reading jeita_cold, rc=%d\n", rc); + return rc; + } + break; + case POWER_SUPPLY_PROP_COOL_TEMP: + rc = fg_get_jeita_threshold(chip, JEITA_COOL, &pval->intval); + if (rc < 0) { + pr_err("Error in reading jeita_cool, rc=%d\n", rc); + return rc; + } + break; + case POWER_SUPPLY_PROP_WARM_TEMP: + rc = fg_get_jeita_threshold(chip, JEITA_WARM, &pval->intval); + if (rc < 0) { + pr_err("Error in reading jeita_warm, rc=%d\n", rc); + return rc; + } + break; + case POWER_SUPPLY_PROP_HOT_TEMP: + rc = fg_get_jeita_threshold(chip, JEITA_HOT, &pval->intval); + if (rc < 0) { + pr_err("Error in reading jeita_hot, rc=%d\n", rc); + return rc; + } + break; case POWER_SUPPLY_PROP_RESISTANCE: rc = fg_get_battery_resistance(chip, &pval->intval); break; @@ -3500,6 +3615,48 @@ static int fg_psy_set_property(struct power_supply *psy, return -EINVAL; } break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + if (chip->cl.active) { + pr_warn("Capacity learning active!\n"); + return 0; + } + if (pval->intval <= 0 || pval->intval > chip->cl.nom_cap_uah) { + pr_err("charge_full is out of bounds\n"); + return -EINVAL; + } + chip->cl.learned_cc_uah = pval->intval; + rc = fg_save_learned_cap_to_sram(chip); + if (rc < 0) + pr_err("Error in saving learned_cc_uah, rc=%d\n", rc); + break; + case POWER_SUPPLY_PROP_COLD_TEMP: + rc = fg_set_jeita_threshold(chip, JEITA_COLD, pval->intval); + if (rc < 0) { + pr_err("Error in writing jeita_cold, rc=%d\n", rc); + return rc; + } + break; + case POWER_SUPPLY_PROP_COOL_TEMP: + rc = fg_set_jeita_threshold(chip, JEITA_COOL, pval->intval); + if (rc < 0) { + pr_err("Error in writing jeita_cool, rc=%d\n", rc); + return rc; + } + break; + case POWER_SUPPLY_PROP_WARM_TEMP: + rc = fg_set_jeita_threshold(chip, JEITA_WARM, pval->intval); + if (rc < 0) { + pr_err("Error in writing jeita_warm, rc=%d\n", rc); + return rc; + } + break; + case POWER_SUPPLY_PROP_HOT_TEMP: + rc = fg_set_jeita_threshold(chip, JEITA_HOT, pval->intval); + if (rc < 0) { + pr_err("Error in writing jeita_hot, rc=%d\n", rc); + return rc; + } + break; default: break; } @@ -3515,6 +3672,11 @@ static int fg_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_CC_STEP: case POWER_SUPPLY_PROP_CC_STEP_SEL: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_COLD_TEMP: + case POWER_SUPPLY_PROP_COOL_TEMP: + case POWER_SUPPLY_PROP_WARM_TEMP: + case POWER_SUPPLY_PROP_HOT_TEMP: return 1; default: break; @@ -3555,7 +3717,12 @@ static int fg_notifier_cb(struct notifier_block *nb, static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_RAW, POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_COLD_TEMP, + POWER_SUPPLY_PROP_COOL_TEMP, + POWER_SUPPLY_PROP_WARM_TEMP, + POWER_SUPPLY_PROP_HOT_TEMP, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_CURRENT_NOW, @@ -3734,29 +3901,29 @@ static int fg_hw_init(struct fg_chip *chip) } } - get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COLD], &val); - rc = fg_write(chip, BATT_INFO_JEITA_TOO_COLD(chip), &val, 1); + rc = fg_set_jeita_threshold(chip, JEITA_COLD, + chip->dt.jeita_thresholds[JEITA_COLD] * 10); if (rc < 0) { pr_err("Error in writing jeita_cold, rc=%d\n", rc); return rc; } - get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COOL], &val); - rc = fg_write(chip, BATT_INFO_JEITA_COLD(chip), &val, 1); + rc = fg_set_jeita_threshold(chip, JEITA_COOL, + chip->dt.jeita_thresholds[JEITA_COOL] * 10); if (rc < 0) { pr_err("Error in writing jeita_cool, rc=%d\n", rc); return rc; } - get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_WARM], &val); - rc = fg_write(chip, BATT_INFO_JEITA_HOT(chip), &val, 1); + rc = fg_set_jeita_threshold(chip, JEITA_WARM, + chip->dt.jeita_thresholds[JEITA_WARM] * 10); if (rc < 0) { pr_err("Error in writing jeita_warm, rc=%d\n", rc); return rc; } - get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_HOT], &val); - rc = fg_write(chip, BATT_INFO_JEITA_TOO_HOT(chip), &val, 1); + rc = fg_set_jeita_threshold(chip, JEITA_HOT, + chip->dt.jeita_thresholds[JEITA_HOT] * 10); if (rc < 0) { pr_err("Error in writing jeita_hot, rc=%d\n", rc); return rc; @@ -4654,6 +4821,9 @@ static int fg_parse_dt(struct fg_chip *chip) chip->dt.hold_soc_while_full = of_property_read_bool(node, "qcom,hold-soc-while-full"); + chip->dt.linearize_soc = of_property_read_bool(node, + "qcom,linearize-soc"); + rc = fg_parse_ki_coefficients(chip); if (rc < 0) pr_err("Error in parsing Ki coefficients, rc=%d\n", rc); @@ -4968,6 +5138,29 @@ static int fg_gen3_remove(struct platform_device *pdev) return 0; } +static void fg_gen3_shutdown(struct platform_device *pdev) +{ + struct fg_chip *chip = dev_get_drvdata(&pdev->dev); + int rc, bsoc; + + if (chip->charge_full) { + rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &bsoc); + if (rc < 0) { + pr_err("Error in getting BATT_SOC, rc=%d\n", rc); + return; + } + + /* We need 2 most significant bytes here */ + bsoc = (u32)bsoc >> 16; + + rc = fg_configure_full_soc(chip, bsoc); + if (rc < 0) { + pr_err("Error in configuring full_soc, rc=%d\n", rc); + return; + } + } +} + static const struct of_device_id fg_gen3_match_table[] = { {.compatible = FG_GEN3_DEV_NAME}, {}, @@ -4982,6 +5175,7 @@ static struct platform_driver fg_gen3_driver = { }, .probe = fg_gen3_probe, .remove = fg_gen3_remove, + .shutdown = fg_gen3_shutdown, }; static int __init fg_gen3_init(void) diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c index b20807990efc..53af3415ec6a 100644 --- a/drivers/power/supply/qcom/qpnp-qnovo.c +++ b/drivers/power/supply/qcom/qpnp-qnovo.c @@ -112,6 +112,7 @@ #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" @@ -130,6 +131,7 @@ struct qnovo_dt_props { bool external_rsense; struct device_node *revid_dev_node; + bool enable_for_dc; }; struct qnovo { @@ -442,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; } @@ -1310,6 +1314,10 @@ static void status_change_work(struct work_struct *work) 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; @@ -1333,6 +1341,26 @@ static void ptrain_restart_work(struct work_struct *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) { @@ -1395,16 +1423,7 @@ static irqreturn_t handle_ptrain_done(int irq, void *data) struct qnovo *chip = data; union power_supply_propval pval = {0}; - /* - * In some cases (esp shutting down) the userspace would disable by - * setting qnovo_enable=0. Also charger could be removed or there is - * an error (i.e. its not okay to run qnovo)- - * skip taking ESR measurement in such situations - */ - - if (get_client_vote(chip->disable_votable, USER_VOTER) - || get_effective_result(chip->not_ok_to_qnovo_votable) > 0) - return IRQ_HANDLED; + qnovo_update_status(chip); /* * hw resets pt_en bit once ptrain_done triggers. @@ -1415,13 +1434,14 @@ static irqreturn_t handle_ptrain_done(int irq, void *data) vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0); vote(chip->pt_dis_votable, ESR_VOTER, true, 0); - if (is_fg_available(chip)) + 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); - qnovo_update_status(chip); kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); return IRQ_HANDLED; } @@ -1434,6 +1454,9 @@ 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); @@ -1706,6 +1729,13 @@ static int qnovo_remove(struct platform_device *pdev) 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", }, { }, @@ -1719,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 c085256a794a..ea205100644d 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -795,6 +795,7 @@ static int smb2_init_usb_main_psy(struct smb2 *chip) *************************/ static enum power_supply_property smb2_dc_props[] = { + POWER_SUPPLY_PROP_INPUT_SUSPEND, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CURRENT_MAX, @@ -810,6 +811,9 @@ static int smb2_dc_get_prop(struct power_supply *psy, int rc = 0; switch (psp) { + case POWER_SUPPLY_PROP_INPUT_SUSPEND: + val->intval = get_effective_result(chg->dc_suspend_votable); + break; case POWER_SUPPLY_PROP_PRESENT: rc = smblib_get_prop_dc_present(chg, val); break; @@ -841,6 +845,10 @@ static int smb2_dc_set_prop(struct power_supply *psy, int rc = 0; switch (psp) { + case POWER_SUPPLY_PROP_INPUT_SUSPEND: + rc = vote(chg->dc_suspend_votable, WBC_VOTER, + (bool)val->intval, 0); + break; case POWER_SUPPLY_PROP_CURRENT_MAX: rc = smblib_set_prop_dc_current_max(chg, val); break; @@ -1191,7 +1199,7 @@ static int smb2_init_batt_psy(struct smb2 *chip) * VBUS REGULATOR REGISTRATION * ******************************/ -struct regulator_ops smb2_vbus_reg_ops = { +static struct regulator_ops smb2_vbus_reg_ops = { .enable = smblib_vbus_regulator_enable, .disable = smblib_vbus_regulator_disable, .is_enabled = smblib_vbus_regulator_is_enabled, @@ -1233,7 +1241,7 @@ static int smb2_init_vbus_regulator(struct smb2 *chip) * VCONN REGULATOR REGISTRATION * ******************************/ -struct regulator_ops smb2_vconn_reg_ops = { +static struct regulator_ops smb2_vconn_reg_ops = { .enable = smblib_vconn_regulator_enable, .disable = smblib_vconn_regulator_disable, .is_enabled = smblib_vconn_regulator_is_enabled, diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index a89d09711ec8..19c0d19106d6 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -65,6 +65,7 @@ enum print_reason { #define OTG_DELAY_VOTER "OTG_DELAY_VOTER" #define USBIN_I_VOTER "USBIN_I_VOTER" #define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER" +#define WBC_VOTER "WBC_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index e7ca1e3fb108..a7e7f0be9afc 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -111,7 +111,7 @@ module_param_named( debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR ); -irqreturn_t smb138x_handle_slave_chg_state_change(int irq, void *data) +static irqreturn_t smb138x_handle_slave_chg_state_change(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb138x *chip = irq_data->parent_data; @@ -727,7 +727,7 @@ static int smb138x_init_parallel_psy(struct smb138x *chip) * VBUS REGULATOR REGISTRATION * ******************************/ -struct regulator_ops smb138x_vbus_reg_ops = { +static struct regulator_ops smb138x_vbus_reg_ops = { .enable = smblib_vbus_regulator_enable, .disable = smblib_vbus_regulator_disable, .is_enabled = smblib_vbus_regulator_is_enabled, @@ -769,7 +769,7 @@ static int smb138x_init_vbus_regulator(struct smb138x *chip) * VCONN REGULATOR REGISTRATION * ******************************/ -struct regulator_ops smb138x_vconn_reg_ops = { +static struct regulator_ops smb138x_vconn_reg_ops = { .enable = smblib_vconn_regulator_enable, .disable = smblib_vconn_regulator_disable, .is_enabled = smblib_vconn_regulator_is_enabled, diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c index 5b41a456c6db..06ecc7ea6e8a 100644 --- a/drivers/power/supply/qcom/step-chg-jeita.c +++ b/drivers/power/supply/qcom/step-chg-jeita.c @@ -443,21 +443,24 @@ int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable) !step_chg_config.prop_name)) { /* fail if step-chg configuration is invalid */ pr_err("Step-chg configuration not defined - fail\n"); - return -ENODATA; + 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"); - return -ENODATA; + 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"); - return -ENODATA; + rc = -ENODATA; + goto release_wakeup_source; } INIT_DELAYED_WORK(&chip->status_change_work, status_change_work); 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/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 907960cfa9d5..829876226689 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -1,6 +1,8 @@ # # QCOM Soc drivers # +source "drivers/soc/qcom/hab/Kconfig" + config MSM_INRUSH_CURRENT_MITIGATION bool "Inrush-current mitigation Driver" help diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 0bf54bedd6ea..229b13a04819 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -107,3 +107,4 @@ obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o obj-$(CONFIG_QCOM_EARLY_RANDOM) += early_random.o obj-$(CONFIG_QCOM_CX_IPEAK) += cx_ipeak.o obj-$(CONFIG_MSM_CACHE_M4M_ERP64) += cache_m4m_erp64.o +obj-$(CONFIG_MSM_HAB) += hab/ diff --git a/drivers/soc/qcom/common_log.c b/drivers/soc/qcom/common_log.c index 1e8744b41e4c..11ca86a4ba41 100644 --- a/drivers/soc/qcom/common_log.c +++ b/drivers/soc/qcom/common_log.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/kmemleak.h> #include <linux/async.h> +#include <linux/thread_info.h> #include <soc/qcom/memory_dump.h> #include <soc/qcom/minidump.h> #include <asm/sections.h> @@ -256,6 +257,32 @@ static void __init register_kernel_sections(void) } } +#ifdef CONFIG_QCOM_MINIDUMP +void dump_stack_minidump(u64 sp) +{ + struct md_region ksp_entry, ktsk_entry; + u32 cpu = smp_processor_id(); + + if (sp < KIMAGE_VADDR || sp > -256UL) + sp = current_stack_pointer; + + sp &= ~(THREAD_SIZE - 1); + scnprintf(ksp_entry.name, sizeof(ksp_entry.name), "KSTACK%d", cpu); + ksp_entry.virt_addr = sp; + ksp_entry.phys_addr = virt_to_phys((uintptr_t *)sp); + ksp_entry.size = THREAD_SIZE; + if (msm_minidump_add_region(&ksp_entry)) + pr_err("Failed to add stack of cpu %d in Minidump\n", cpu); + + scnprintf(ktsk_entry.name, sizeof(ktsk_entry.name), "KTASK%d", cpu); + ktsk_entry.virt_addr = (u64)current; + ktsk_entry.phys_addr = virt_to_phys((uintptr_t *)current); + ktsk_entry.size = sizeof(struct task_struct); + if (msm_minidump_add_region(&ktsk_entry)) + pr_err("Failed to add current task %d in Minidump\n", cpu); +} +#endif + static void __init async_common_log_init(void *data, async_cookie_t cookie) { register_kernel_sections(); diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c index e0d9f68ceef9..f3debd14c27b 100644 --- a/drivers/soc/qcom/glink.c +++ b/drivers/soc/qcom/glink.c @@ -2372,6 +2372,35 @@ static void dummy_tx_cmd_ch_remote_close_ack(struct glink_transport_if *if_ptr, } /** + * dummy_tx_cmd_ch_open() - dummy channel open cmd sending function + * @if_ptr: The transport to transmit on. + * @lcid: The local channel id to encode. + * @name: The channel name to encode. + * @req_xprt: The transport the core would like to migrate this channel to. + * + * Return: 0 on success or standard Linux error code. + */ +static int dummy_tx_cmd_ch_open(struct glink_transport_if *if_ptr, + uint32_t lcid, const char *name, + uint16_t req_xprt) +{ + return -EOPNOTSUPP; +} + +/** + * dummy_tx_cmd_ch_remote_open_ack() - convert a channel open ack cmd to wire + * format and transmit + * @if_ptr: The transport to transmit on. + * @rcid: The remote channel id to encode. + * @xprt_resp: The response to a transport migration request. + */ +static void dummy_tx_cmd_ch_remote_open_ack(struct glink_transport_if *if_ptr, + uint32_t rcid, uint16_t xprt_resp) +{ + /* intentionally left blank */ +} + +/** * dummy_get_power_vote_ramp_time() - Dummy Power vote ramp time * @if_ptr: The transport to transmit on. * @state: The power state being requested from the transport. @@ -4184,8 +4213,14 @@ static struct glink_core_xprt_ctx *glink_create_dummy_xprt_ctx( if_ptr->tx_cmd_remote_rx_intent_req_ack = dummy_tx_cmd_remote_rx_intent_req_ack; if_ptr->tx_cmd_set_sigs = dummy_tx_cmd_set_sigs; + if_ptr->tx_cmd_ch_open = dummy_tx_cmd_ch_open; + if_ptr->tx_cmd_ch_remote_open_ack = dummy_tx_cmd_ch_remote_open_ack; if_ptr->tx_cmd_ch_close = dummy_tx_cmd_ch_close; if_ptr->tx_cmd_ch_remote_close_ack = dummy_tx_cmd_ch_remote_close_ack; + if_ptr->tx_cmd_tracer_pkt = dummy_tx_cmd_tracer_pkt; + if_ptr->get_power_vote_ramp_time = dummy_get_power_vote_ramp_time; + if_ptr->power_vote = dummy_power_vote; + if_ptr->power_unvote = dummy_power_unvote; xprt_ptr->ops = if_ptr; xprt_ptr->log_ctx = log_ctx; 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/hab/Kconfig b/drivers/soc/qcom/hab/Kconfig new file mode 100644 index 000000000000..2e4f5114e29f --- /dev/null +++ b/drivers/soc/qcom/hab/Kconfig @@ -0,0 +1,7 @@ +config MSM_HAB + bool "Enable Multimedia driver Hypervisor Abstraction Layer" + help + Multimedia driver hypervisor abstraction layer. + Required for drivers to use the HAB API to communicate with the host + OS. + diff --git a/drivers/soc/qcom/hab/Makefile b/drivers/soc/qcom/hab/Makefile new file mode 100644 index 000000000000..83fc54d42202 --- /dev/null +++ b/drivers/soc/qcom/hab/Makefile @@ -0,0 +1,14 @@ +msm_hab-objs = \ + khab.o \ + hab.o \ + hab_msg.o \ + hab_vchan.o \ + hab_pchan.o \ + hab_open.o \ + hab_mimex.o \ + hab_mem_linux.o \ + hab_pipe.o \ + qvm_comm.o \ + hab_qvm.o + +obj-$(CONFIG_MSM_HAB) += msm_hab.o diff --git a/drivers/soc/qcom/hab/hab.c b/drivers/soc/qcom/hab/hab.c new file mode 100644 index 000000000000..c6df36f5c0a2 --- /dev/null +++ b/drivers/soc/qcom/hab/hab.c @@ -0,0 +1,726 @@ +/* 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 "hab.h" + +#define HAB_DEVICE_CNSTR(__name__, __id__, __num__) { \ + .name = __name__,\ + .id = __id__,\ + .pchannels = LIST_HEAD_INIT(hab_devices[__num__].pchannels),\ + .pchan_lock = __MUTEX_INITIALIZER(hab_devices[__num__].pchan_lock),\ + .openq_list = LIST_HEAD_INIT(hab_devices[__num__].openq_list),\ + .openlock = __SPIN_LOCK_UNLOCKED(&hab_devices[__num__].openlock)\ + } + +/* the following has to match habmm definitions, order does not matter */ +static struct hab_device hab_devices[] = { + HAB_DEVICE_CNSTR(DEVICE_AUD1_NAME, MM_AUD_1, 0), + HAB_DEVICE_CNSTR(DEVICE_AUD2_NAME, MM_AUD_2, 1), + HAB_DEVICE_CNSTR(DEVICE_AUD3_NAME, MM_AUD_3, 2), + HAB_DEVICE_CNSTR(DEVICE_AUD4_NAME, MM_AUD_4, 3), + HAB_DEVICE_CNSTR(DEVICE_CAM_NAME, MM_CAM, 4), + HAB_DEVICE_CNSTR(DEVICE_DISP1_NAME, MM_DISP_1, 5), + HAB_DEVICE_CNSTR(DEVICE_DISP2_NAME, MM_DISP_2, 6), + HAB_DEVICE_CNSTR(DEVICE_DISP3_NAME, MM_DISP_3, 7), + HAB_DEVICE_CNSTR(DEVICE_DISP4_NAME, MM_DISP_4, 8), + HAB_DEVICE_CNSTR(DEVICE_DISP5_NAME, MM_DISP_5, 9), + HAB_DEVICE_CNSTR(DEVICE_GFX_NAME, MM_GFX, 10), + HAB_DEVICE_CNSTR(DEVICE_VID_NAME, MM_VID, 11), + HAB_DEVICE_CNSTR(DEVICE_MISC_NAME, MM_MISC, 12), + HAB_DEVICE_CNSTR(DEVICE_QCPE1_NAME, MM_QCPE_VM1, 13), + HAB_DEVICE_CNSTR(DEVICE_QCPE2_NAME, MM_QCPE_VM2, 14), + HAB_DEVICE_CNSTR(DEVICE_QCPE3_NAME, MM_QCPE_VM3, 15), + HAB_DEVICE_CNSTR(DEVICE_QCPE4_NAME, MM_QCPE_VM4, 16) +}; + +struct hab_driver hab_driver = { + .ndevices = ARRAY_SIZE(hab_devices), + .devp = hab_devices, +}; + +struct uhab_context *hab_ctx_alloc(int kernel) +{ + struct uhab_context *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + + ctx->closing = 0; + INIT_LIST_HEAD(&ctx->vchannels); + INIT_LIST_HEAD(&ctx->exp_whse); + INIT_LIST_HEAD(&ctx->imp_whse); + + INIT_LIST_HEAD(&ctx->exp_rxq); + init_waitqueue_head(&ctx->exp_wq); + spin_lock_init(&ctx->expq_lock); + + spin_lock_init(&ctx->imp_lock); + rwlock_init(&ctx->exp_lock); + rwlock_init(&ctx->ctx_lock); + + kref_init(&ctx->refcount); + ctx->import_ctx = habmem_imp_hyp_open(); + if (!ctx->import_ctx) { + kfree(ctx); + return NULL; + } + ctx->kernel = kernel; + + return ctx; +} + +void hab_ctx_free(struct kref *ref) +{ + struct uhab_context *ctx = + container_of(ref, struct uhab_context, refcount); + struct hab_export_ack_recvd *ack_recvd, *tmp; + + habmem_imp_hyp_close(ctx->import_ctx, ctx->kernel); + + list_for_each_entry_safe(ack_recvd, tmp, &ctx->exp_rxq, node) { + list_del(&ack_recvd->node); + kfree(ack_recvd); + } + + kfree(ctx); +} + +struct virtual_channel *hab_get_vchan_fromvcid(int32_t vcid, + struct uhab_context *ctx) +{ + struct virtual_channel *vchan; + + read_lock(&ctx->ctx_lock); + list_for_each_entry(vchan, &ctx->vchannels, node) { + if (vcid == vchan->id) { + kref_get(&vchan->refcount); + read_unlock(&ctx->ctx_lock); + return vchan; + } + } + read_unlock(&ctx->ctx_lock); + return NULL; +} + +static struct hab_device *find_hab_device(unsigned int mm_id) +{ + int i; + + for (i = 0; i < hab_driver.ndevices; i++) { + if (hab_driver.devp[i].id == HAB_MMID_GET_MAJOR(mm_id)) + return &hab_driver.devp[i]; + } + + pr_err("find_hab_device failed: id=%d\n", mm_id); + return NULL; +} +/* + * open handshake in FE and BE + + * frontend backend + * send(INIT) wait(INIT) + * wait(INIT_ACK) send(INIT_ACK) + * send(ACK) wait(ACK) + + */ +struct virtual_channel *frontend_open(struct uhab_context *ctx, + unsigned int mm_id, + int dom_id) +{ + int ret, open_id = 0; + struct physical_channel *pchan = NULL; + struct hab_device *dev; + struct virtual_channel *vchan = NULL; + static atomic_t open_id_counter = ATOMIC_INIT(0); + struct hab_open_request request; + struct hab_open_request *recv_request; + int sub_id = HAB_MMID_GET_MINOR(mm_id); + + dev = find_hab_device(mm_id); + if (dev == NULL) { + ret = -EINVAL; + goto err; + } + + pchan = hab_pchan_find_domid(dev, dom_id); + if (!pchan) { + pr_err("hab_pchan_find_domid failed: dom_id=%d\n", dom_id); + ret = -EINVAL; + goto err; + } + + vchan = hab_vchan_alloc(ctx, pchan); + if (!vchan) { + ret = -ENOMEM; + goto err; + } + + /* Send Init sequence */ + open_id = atomic_inc_return(&open_id_counter); + hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT, pchan, + vchan->id, sub_id, open_id); + ret = hab_open_request_send(&request); + if (ret) { + pr_err("hab_open_request_send failed: %d\n", ret); + goto err; + } + + /* Wait for Init-Ack sequence */ + hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_ACK, pchan, + 0, sub_id, open_id); + ret = hab_open_listen(ctx, dev, &request, &recv_request, 0); + if (ret || !recv_request) { + pr_err("hab_open_listen failed: %d\n", ret); + goto err; + } + + vchan->otherend_id = recv_request->vchan_id; + hab_open_request_free(recv_request); + + /* Send Ack sequence */ + hab_open_request_init(&request, HAB_PAYLOAD_TYPE_ACK, pchan, + 0, sub_id, open_id); + ret = hab_open_request_send(&request); + if (ret) + goto err; + + hab_pchan_put(pchan); + + return vchan; +err: + if (vchan) + hab_vchan_put(vchan); + if (pchan) + hab_pchan_put(pchan); + + return ERR_PTR(ret); +} + +struct virtual_channel *backend_listen(struct uhab_context *ctx, + unsigned int mm_id) +{ + int ret; + int open_id; + int sub_id = HAB_MMID_GET_MINOR(mm_id); + struct physical_channel *pchan = NULL; + struct hab_device *dev; + struct virtual_channel *vchan = NULL; + struct hab_open_request request; + struct hab_open_request *recv_request; + uint32_t otherend_vchan_id; + + dev = find_hab_device(mm_id); + if (dev == NULL) { + ret = -EINVAL; + goto err; + } + + while (1) { + /* Wait for Init sequence */ + hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT, + NULL, 0, sub_id, 0); + ret = hab_open_listen(ctx, dev, &request, &recv_request, 0); + if (ret || !recv_request) { + pr_err("hab_open_listen failed: %d\n", ret); + goto err; + } + + otherend_vchan_id = recv_request->vchan_id; + open_id = recv_request->open_id; + pchan = recv_request->pchan; + hab_pchan_get(pchan); + hab_open_request_free(recv_request); + + vchan = hab_vchan_alloc(ctx, pchan); + if (!vchan) { + ret = -ENOMEM; + goto err; + } + + vchan->otherend_id = otherend_vchan_id; + + /* Send Init-Ack sequence */ + hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_ACK, + pchan, vchan->id, sub_id, open_id); + ret = hab_open_request_send(&request); + if (ret) + goto err; + + /* Wait for Ack sequence */ + hab_open_request_init(&request, HAB_PAYLOAD_TYPE_ACK, + pchan, 0, sub_id, open_id); + ret = hab_open_listen(ctx, dev, &request, &recv_request, HZ); + + if (ret != -EAGAIN) + break; + + hab_vchan_put(vchan); + vchan = NULL; + hab_pchan_put(pchan); + pchan = NULL; + } + + if (ret || !recv_request) { + pr_err("backend_listen failed: %d\n", ret); + ret = -EINVAL; + goto err; + } + + hab_open_request_free(recv_request); + hab_pchan_put(pchan); + return vchan; +err: + if (vchan) + hab_vchan_put(vchan); + if (pchan) + hab_pchan_put(pchan); + return ERR_PTR(ret); +} + +long hab_vchan_send(struct uhab_context *ctx, + int vcid, + size_t sizebytes, + void *data, + unsigned int flags) +{ + struct virtual_channel *vchan; + int ret; + struct hab_header header = HAB_HEADER_INITIALIZER; + int nonblocking_flag = flags & HABMM_SOCKET_SEND_FLAGS_NON_BLOCKING; + + if (sizebytes > HAB_MAX_MSG_SIZEBYTES) { + pr_err("Message too large, %lu bytes\n", sizebytes); + return -EINVAL; + } + + vchan = hab_get_vchan_fromvcid(vcid, ctx); + if (!vchan || vchan->otherend_closed) + return -ENODEV; + + HAB_HEADER_SET_SIZE(header, sizebytes); + HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_MSG); + HAB_HEADER_SET_ID(header, vchan->otherend_id); + + while (1) { + ret = physical_channel_send(vchan->pchan, &header, data); + + if (vchan->otherend_closed || nonblocking_flag || + ret != -EAGAIN) + break; + + schedule(); + } + + hab_vchan_put(vchan); + return ret; +} + +struct hab_message *hab_vchan_recv(struct uhab_context *ctx, + int vcid, + unsigned int flags) +{ + struct virtual_channel *vchan; + struct hab_message *message; + int ret = 0; + int nonblocking_flag = flags & HABMM_SOCKET_RECV_FLAGS_NON_BLOCKING; + + vchan = hab_get_vchan_fromvcid(vcid, ctx); + if (!vchan || vchan->otherend_closed) + return ERR_PTR(-ENODEV); + + if (nonblocking_flag) { + /* + * Try to pull data from the ring in this context instead of + * IRQ handler. Any available messages will be copied and queued + * internally, then fetched by hab_msg_dequeue() + */ + physical_channel_rx_dispatch((unsigned long) vchan->pchan); + } + + message = hab_msg_dequeue(vchan, !nonblocking_flag); + if (!message) { + if (nonblocking_flag) + ret = -EAGAIN; + else + ret = -EPIPE; + } + + hab_vchan_put(vchan); + return ret ? ERR_PTR(ret) : message; +} + +bool hab_is_loopback(void) +{ + return hab_driver.b_loopback; +} + +int hab_vchan_open(struct uhab_context *ctx, + unsigned int mmid, + int32_t *vcid, + uint32_t flags) +{ + struct virtual_channel *vchan; + + if (!vcid) + return -EINVAL; + + if (hab_is_loopback()) { + if (!hab_driver.loopback_num) { + hab_driver.loopback_num = 1; + vchan = backend_listen(ctx, mmid); + } else { + hab_driver.loopback_num = 0; + vchan = frontend_open(ctx, mmid, LOOPBACK_DOM); + } + } else { + if (hab_driver.b_server_dom) + vchan = backend_listen(ctx, mmid); + else + vchan = frontend_open(ctx, mmid, 0); + } + + if (IS_ERR(vchan)) + return PTR_ERR(vchan); + + write_lock(&ctx->ctx_lock); + list_add_tail(&vchan->node, &ctx->vchannels); + write_unlock(&ctx->ctx_lock); + + *vcid = vchan->id; + + return 0; +} + +void hab_send_close_msg(struct virtual_channel *vchan) +{ + struct hab_header header; + + if (vchan && !vchan->otherend_closed) { + HAB_HEADER_SET_SIZE(header, 0); + HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_CLOSE); + HAB_HEADER_SET_ID(header, vchan->otherend_id); + physical_channel_send(vchan->pchan, &header, NULL); + } +} + +static void hab_vchan_close_impl(struct kref *ref) +{ + struct virtual_channel *vchan = + container_of(ref, struct virtual_channel, usagecnt); + + list_del(&vchan->node); + hab_vchan_stop_notify(vchan); + hab_vchan_put(vchan); +} + + +void hab_vchan_close(struct uhab_context *ctx, int32_t vcid) +{ + struct virtual_channel *vchan, *tmp; + + if (!ctx) + return; + + write_lock(&ctx->ctx_lock); + list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) { + if (vchan->id == vcid) { + kref_put(&vchan->usagecnt, hab_vchan_close_impl); + break; + } + } + + write_unlock(&ctx->ctx_lock); +} + +static int hab_open(struct inode *inodep, struct file *filep) +{ + int result = 0; + struct uhab_context *ctx; + + ctx = hab_ctx_alloc(0); + + if (!ctx) { + pr_err("hab_ctx_alloc failed\n"); + filep->private_data = NULL; + return -ENOMEM; + } + + filep->private_data = ctx; + + return result; +} + +static int hab_release(struct inode *inodep, struct file *filep) +{ + struct uhab_context *ctx = filep->private_data; + struct virtual_channel *vchan, *tmp; + + if (!ctx) + return 0; + + write_lock(&ctx->ctx_lock); + + list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) { + list_del(&vchan->node); + hab_vchan_stop_notify(vchan); + hab_vchan_put(vchan); + } + + write_unlock(&ctx->ctx_lock); + + hab_ctx_put(ctx); + filep->private_data = NULL; + + return 0; +} + +static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + struct uhab_context *ctx = (struct uhab_context *)filep->private_data; + struct hab_open *open_param; + struct hab_close *close_param; + struct hab_recv *recv_param; + struct hab_send *send_param; + struct hab_message *msg; + void *send_data; + unsigned char data[256] = { 0 }; + long ret = 0; + + if (_IOC_SIZE(cmd) && (cmd & IOC_IN)) { + if (_IOC_SIZE(cmd) > sizeof(data)) + return -EINVAL; + + if (copy_from_user(data, (void __user *)arg, _IOC_SIZE(cmd))) { + pr_err("copy_from_user failed cmd=%x size=%d\n", + cmd, _IOC_SIZE(cmd)); + return -EFAULT; + } + } + + switch (cmd) { + case IOCTL_HAB_VC_OPEN: + open_param = (struct hab_open *)data; + ret = hab_vchan_open(ctx, open_param->mmid, + &open_param->vcid, open_param->flags); + break; + case IOCTL_HAB_VC_CLOSE: + close_param = (struct hab_close *)data; + hab_vchan_close(ctx, close_param->vcid); + break; + case IOCTL_HAB_SEND: + send_param = (struct hab_send *)data; + if (send_param->sizebytes > HAB_MAX_MSG_SIZEBYTES) { + ret = -EINVAL; + break; + } + + send_data = kzalloc(send_param->sizebytes, GFP_TEMPORARY); + if (!send_data) { + ret = -ENOMEM; + break; + } + + if (copy_from_user(send_data, (void __user *)send_param->data, + send_param->sizebytes)) { + ret = -EFAULT; + } else { + ret = hab_vchan_send(ctx, send_param->vcid, + send_param->sizebytes, + send_data, + send_param->flags); + } + kfree(send_data); + break; + case IOCTL_HAB_RECV: + recv_param = (struct hab_recv *)data; + if (!recv_param->data) { + ret = -EINVAL; + break; + } + + msg = hab_vchan_recv(ctx, recv_param->vcid, recv_param->flags); + + if (IS_ERR(msg)) { + recv_param->sizebytes = 0; + ret = PTR_ERR(msg); + break; + } + + if (recv_param->sizebytes < msg->sizebytes) { + recv_param->sizebytes = 0; + ret = -EINVAL; + } else if (copy_to_user((void __user *)recv_param->data, + msg->data, + msg->sizebytes)) { + pr_err("copy_to_user failed: vc=%x size=%d\n", + recv_param->vcid, (int)msg->sizebytes); + recv_param->sizebytes = 0; + ret = -EFAULT; + } else { + recv_param->sizebytes = msg->sizebytes; + } + + hab_msg_free(msg); + break; + case IOCTL_HAB_VC_EXPORT: + ret = hab_mem_export(ctx, (struct hab_export *)data, 0); + break; + case IOCTL_HAB_VC_IMPORT: + ret = hab_mem_import(ctx, (struct hab_import *)data, 0); + break; + case IOCTL_HAB_VC_UNEXPORT: + ret = hab_mem_unexport(ctx, (struct hab_unexport *)data, 0); + break; + case IOCTL_HAB_VC_UNIMPORT: + ret = hab_mem_unimport(ctx, (struct hab_unimport *)data, 0); + break; + default: + ret = -ENOIOCTLCMD; + } + + if (ret == 0 && _IOC_SIZE(cmd) && (cmd & IOC_OUT)) + if (copy_to_user((void __user *) arg, data, _IOC_SIZE(cmd))) { + pr_err("copy_to_user failed: cmd=%x\n", cmd); + ret = -EFAULT; + } + + return ret; +} + +static const struct file_operations hab_fops = { + .owner = THIS_MODULE, + .open = hab_open, + .release = hab_release, + .mmap = habmem_imp_hyp_mmap, + .unlocked_ioctl = hab_ioctl +}; + +/* + * These map sg functions are pass through because the memory backing the + * sg list is already accessible to the kernel as they come from a the + * dedicated shared vm pool + */ + +static int hab_map_sg(struct device *dev, struct scatterlist *sgl, + int nelems, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + /* return nelems directly */ + return nelems; +} + +static void hab_unmap_sg(struct device *dev, + struct scatterlist *sgl, int nelems, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + /*Do nothing */ +} + +static const struct dma_map_ops hab_dma_ops = { + .map_sg = hab_map_sg, + .unmap_sg = hab_unmap_sg, +}; + +static int __init hab_init(void) +{ + int result; + int i; + dev_t dev; + struct hab_device *device; + + result = alloc_chrdev_region(&hab_driver.major, 0, 1, "hab"); + + if (result < 0) { + pr_err("alloc_chrdev_region failed: %d\n", result); + return result; + } + + cdev_init(&hab_driver.cdev, &hab_fops); + hab_driver.cdev.owner = THIS_MODULE; + hab_driver.cdev.ops = &hab_fops; + dev = MKDEV(MAJOR(hab_driver.major), 0); + + result = cdev_add(&hab_driver.cdev, dev, 1); + + if (result < 0) { + unregister_chrdev_region(dev, 1); + pr_err("cdev_add failed: %d\n", result); + return result; + } + + hab_driver.class = class_create(THIS_MODULE, "hab"); + + if (IS_ERR(hab_driver.class)) { + result = PTR_ERR(hab_driver.class); + pr_err("class_create failed: %d\n", result); + goto err; + } + + hab_driver.dev = device_create(hab_driver.class, NULL, + dev, &hab_driver, "hab"); + + if (IS_ERR(hab_driver.dev)) { + result = PTR_ERR(hab_driver.dev); + pr_err("device_create failed: %d\n", result); + goto err; + } + + for (i = 0; i < hab_driver.ndevices; i++) { + device = &hab_driver.devp[i]; + init_waitqueue_head(&device->openq); + } + + hab_hypervisor_register(); + + hab_driver.kctx = hab_ctx_alloc(1); + if (!hab_driver.kctx) { + pr_err("hab_ctx_alloc failed"); + result = -ENOMEM; + hab_hypervisor_unregister(); + goto err; + } + + set_dma_ops(hab_driver.dev, &hab_dma_ops); + + return result; + +err: + if (!IS_ERR_OR_NULL(hab_driver.dev)) + device_destroy(hab_driver.class, dev); + if (!IS_ERR_OR_NULL(hab_driver.class)) + class_destroy(hab_driver.class); + cdev_del(&hab_driver.cdev); + unregister_chrdev_region(dev, 1); + + return result; +} + +static void __exit hab_exit(void) +{ + dev_t dev; + + hab_hypervisor_unregister(); + hab_ctx_put(hab_driver.kctx); + dev = MKDEV(MAJOR(hab_driver.major), 0); + device_destroy(hab_driver.class, dev); + class_destroy(hab_driver.class); + cdev_del(&hab_driver.cdev); + unregister_chrdev_region(dev, 1); +} + +subsys_initcall(hab_init); +module_exit(hab_exit); + +MODULE_DESCRIPTION("Hypervisor abstraction layer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/hab/hab.h b/drivers/soc/qcom/hab/hab.h new file mode 100644 index 000000000000..805e5b4a7008 --- /dev/null +++ b/drivers/soc/qcom/hab/hab.h @@ -0,0 +1,415 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __HAB_H +#define __HAB_H + +#define pr_fmt(fmt) "hab: " fmt + +#include <linux/types.h> + +#include <linux/habmm.h> + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/sched.h> +#include <linux/cdev.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/rbtree.h> +#include <linux/idr.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> + +enum hab_payload_type { + HAB_PAYLOAD_TYPE_MSG = 0x0, + HAB_PAYLOAD_TYPE_INIT, + HAB_PAYLOAD_TYPE_INIT_ACK, + HAB_PAYLOAD_TYPE_ACK, + HAB_PAYLOAD_TYPE_EXPORT, + HAB_PAYLOAD_TYPE_EXPORT_ACK, + HAB_PAYLOAD_TYPE_PROFILE, + HAB_PAYLOAD_TYPE_CLOSE, +}; +#define LOOPBACK_DOM 0xFF + +/* + * Tuning required. If there are multiple clients, the aging of previous + * "request" might be discarded + */ +#define Q_AGE_THRESHOLD 1000000 + +/* match the name to dtsi if for real HYP framework */ +#define DEVICE_AUD1_NAME "hab_aud1" +#define DEVICE_AUD2_NAME "hab_aud2" +#define DEVICE_AUD3_NAME "hab_aud3" +#define DEVICE_AUD4_NAME "hab_aud4" +#define DEVICE_CAM_NAME "hab_cam" +#define DEVICE_DISP1_NAME "hab_disp1" +#define DEVICE_DISP2_NAME "hab_disp2" +#define DEVICE_DISP3_NAME "hab_disp3" +#define DEVICE_DISP4_NAME "hab_disp4" +#define DEVICE_DISP5_NAME "hab_disp5" +#define DEVICE_GFX_NAME "hab_ogles" +#define DEVICE_VID_NAME "hab_vid" +#define DEVICE_MISC_NAME "hab_misc" +#define DEVICE_QCPE1_NAME "hab_qcpe_vm1" +#define DEVICE_QCPE2_NAME "hab_qcpe_vm2" +#define DEVICE_QCPE3_NAME "hab_qcpe_vm3" +#define DEVICE_QCPE4_NAME "hab_qcpe_vm4" + +/* "Size" of the HAB_HEADER_ID and HAB_VCID_ID must match */ +#define HAB_HEADER_SIZE_SHIFT 0 +#define HAB_HEADER_TYPE_SHIFT 16 +#define HAB_HEADER_ID_SHIFT 24 +#define HAB_HEADER_SIZE_MASK 0x0000FFFF +#define HAB_HEADER_TYPE_MASK 0x00FF0000 +#define HAB_HEADER_ID_MASK 0xFF000000 +#define HAB_HEADER_INITIALIZER {0} + +#define HAB_MMID_GET_MAJOR(mmid) (mmid & 0xFFFF) +#define HAB_MMID_GET_MINOR(mmid) ((mmid>>16) & 0xFF) + +#define HAB_VCID_ID_SHIFT 0 +#define HAB_VCID_DOMID_SHIFT 8 +#define HAB_VCID_MMID_SHIFT 16 +#define HAB_VCID_ID_MASK 0x000000FF +#define HAB_VCID_DOMID_MASK 0x0000FF00 +#define HAB_VCID_MMID_MASK 0xFFFF0000 +#define HAB_VCID_GET_ID(vcid) \ + (((vcid) & HAB_VCID_ID_MASK) >> HAB_VCID_ID_SHIFT) + +#define HAB_HEADER_SET_SIZE(header, size) \ + ((header).info = (((header).info) & (~HAB_HEADER_SIZE_MASK)) | \ + (((size) << HAB_HEADER_SIZE_SHIFT) & HAB_HEADER_SIZE_MASK)) + +#define HAB_HEADER_SET_TYPE(header, type) \ + ((header).info = (((header).info) & (~HAB_HEADER_TYPE_MASK)) | \ + (((type) << HAB_HEADER_TYPE_SHIFT) & HAB_HEADER_TYPE_MASK)) + +#define HAB_HEADER_SET_ID(header, id) \ + ((header).info = (((header).info) & (~HAB_HEADER_ID_MASK)) | \ + ((HAB_VCID_GET_ID(id) << HAB_HEADER_ID_SHIFT) \ + & HAB_HEADER_ID_MASK)) + +#define HAB_HEADER_GET_SIZE(header) \ + ((((header).info) & HAB_HEADER_SIZE_MASK) >> HAB_HEADER_SIZE_SHIFT) + +#define HAB_HEADER_GET_TYPE(header) \ + ((((header).info) & HAB_HEADER_TYPE_MASK) >> HAB_HEADER_TYPE_SHIFT) + +#define HAB_HEADER_GET_ID(header) \ + (((((header).info) & HAB_HEADER_ID_MASK) >> \ + (HAB_HEADER_ID_SHIFT - HAB_VCID_ID_SHIFT)) & HAB_VCID_ID_MASK) + +struct hab_header { + uint32_t info; +}; + +struct physical_channel { + struct kref refcount; + struct hab_device *habdev; + struct list_head node; + struct idr vchan_idr; + spinlock_t vid_lock; + + struct idr expid_idr; + spinlock_t expid_lock; + + void *hyp_data; + int dom_id; + int closed; + + spinlock_t rxbuf_lock; +}; + +struct hab_open_send_data { + int vchan_id; + int sub_id; + int open_id; +}; + +struct hab_open_request { + int type; + struct physical_channel *pchan; + int vchan_id; + int sub_id; + int open_id; +}; + +struct hab_open_node { + struct hab_open_request request; + struct list_head node; + int age; +}; + +struct hab_export_ack { + uint32_t export_id; + int32_t vcid_local; + int32_t vcid_remote; +}; + +struct hab_export_ack_recvd { + struct hab_export_ack ack; + struct list_head node; + int age; +}; + +struct hab_message { + size_t sizebytes; + struct list_head node; + uint32_t data[]; +}; + +struct hab_device { + const char *name; + unsigned int id; + struct list_head pchannels; + struct mutex pchan_lock; + struct list_head openq_list; + spinlock_t openlock; + wait_queue_head_t openq; +}; + +struct uhab_context { + struct kref refcount; + struct list_head vchannels; + + struct list_head exp_whse; + uint32_t export_total; + + wait_queue_head_t exp_wq; + struct list_head exp_rxq; + rwlock_t exp_lock; + spinlock_t expq_lock; + + struct list_head imp_whse; + spinlock_t imp_lock; + uint32_t import_total; + + void *import_ctx; + + rwlock_t ctx_lock; + int closing; + int kernel; +}; + +struct hab_driver { + struct device *dev; + struct cdev cdev; + dev_t major; + struct class *class; + int irq; + + int ndevices; + struct hab_device *devp; + struct uhab_context *kctx; + int b_server_dom; + int loopback_num; + int b_loopback; +}; + +struct virtual_channel { + struct work_struct work; + /* + * refcount is used to track the references from hab core to the virtual + * channel such as references from physical channels, + * i.e. references from the "other" side + */ + struct kref refcount; + /* + * usagecnt is used to track the clients who are using this virtual + * channel such as local clients, client sowftware etc, + * i.e. references from "this" side + */ + struct kref usagecnt; + struct physical_channel *pchan; + struct uhab_context *ctx; + struct list_head node; + struct list_head rx_list; + wait_queue_head_t rx_queue; + spinlock_t rx_lock; + int id; + int otherend_id; + int otherend_closed; +}; + +/* + * Struct shared between local and remote, contents are composed by exporter, + * the importer only writes to pdata and local (exporter) domID + */ +struct export_desc { + uint32_t export_id; + int readonly; + uint64_t import_index; + + struct virtual_channel *vchan; + + int32_t vcid_local; + int32_t vcid_remote; + int domid_local; + int domid_remote; + + struct list_head node; + void *kva; + int payload_count; + unsigned char payload[1]; +}; + +int hab_vchan_open(struct uhab_context *ctx, + unsigned int mmid, int32_t *vcid, uint32_t flags); +void hab_vchan_close(struct uhab_context *ctx, + int32_t vcid); +long hab_vchan_send(struct uhab_context *ctx, + int vcid, + size_t sizebytes, + void *data, + unsigned int flags); +struct hab_message *hab_vchan_recv(struct uhab_context *ctx, + int vcid, + unsigned int flags); +void hab_vchan_stop(struct virtual_channel *vchan); +void hab_vchan_stop_notify(struct virtual_channel *vchan); + +int hab_mem_export(struct uhab_context *ctx, + struct hab_export *param, int kernel); +int hab_mem_import(struct uhab_context *ctx, + struct hab_import *param, int kernel); +int hab_mem_unexport(struct uhab_context *ctx, + struct hab_unexport *param, int kernel); +int hab_mem_unimport(struct uhab_context *ctx, + struct hab_unimport *param, int kernel); + +void habmem_remove_export(struct export_desc *exp); + +/* memory hypervisor framework plugin I/F */ +void *habmm_hyp_allocate_grantable(int page_count, + uint32_t *sizebytes); + +int habmem_hyp_grant_user(unsigned long address, + int page_count, + int flags, + int remotedom, + void *ppdata); + +int habmem_hyp_grant(unsigned long address, + int page_count, + int flags, + int remotedom, + void *ppdata); + +int habmem_hyp_revoke(void *expdata, uint32_t count); + +void *habmem_imp_hyp_open(void); +void habmem_imp_hyp_close(void *priv, int kernel); + +long habmem_imp_hyp_map(void *priv, void *impdata, uint32_t count, + uint32_t remotedom, + uint64_t *index, + void **pkva, + int kernel, + uint32_t userflags); + +long habmm_imp_hyp_unmap(void *priv, uint64_t index, + uint32_t count, + int kernel); + +int habmem_imp_hyp_mmap(struct file *flip, struct vm_area_struct *vma); + + + +void hab_msg_free(struct hab_message *message); +struct hab_message *hab_msg_dequeue(struct virtual_channel *vchan, + int wait_flag); + +void hab_msg_recv(struct physical_channel *pchan, + struct hab_header *header); + +void hab_open_request_init(struct hab_open_request *request, + int type, + struct physical_channel *pchan, + int vchan_id, + int sub_id, + int open_id); +int hab_open_request_send(struct hab_open_request *request); +int hab_open_request_add(struct physical_channel *pchan, + struct hab_header *header); +void hab_open_request_free(struct hab_open_request *request); +int hab_open_listen(struct uhab_context *ctx, + struct hab_device *dev, + struct hab_open_request *listen, + struct hab_open_request **recv_request, + int ms_timeout); + +struct virtual_channel *hab_vchan_alloc(struct uhab_context *ctx, + struct physical_channel *pchan); +struct virtual_channel *hab_vchan_get(struct physical_channel *pchan, + uint32_t vchan_id); +void hab_vchan_put(struct virtual_channel *vchan); + +struct virtual_channel *hab_get_vchan_fromvcid(int32_t vcid, + struct uhab_context *ctx); +struct physical_channel *hab_pchan_alloc(struct hab_device *habdev, + int otherend_id); +struct physical_channel *hab_pchan_find_domid(struct hab_device *dev, + int dom_id); +int hab_vchan_find_domid(struct virtual_channel *vchan); + +void hab_pchan_get(struct physical_channel *pchan); +void hab_pchan_put(struct physical_channel *pchan); + +struct uhab_context *hab_ctx_alloc(int kernel); + +void hab_ctx_free(struct kref *ref); + +static inline void hab_ctx_get(struct uhab_context *ctx) +{ + if (ctx) + kref_get(&ctx->refcount); +} + +static inline void hab_ctx_put(struct uhab_context *ctx) +{ + if (ctx) + kref_put(&ctx->refcount, hab_ctx_free); +} + +void hab_send_close_msg(struct virtual_channel *vchan); +int hab_hypervisor_register(void); +void hab_hypervisor_unregister(void); + +int physical_channel_read(struct physical_channel *pchan, + void *payload, + size_t read_size); + +int physical_channel_send(struct physical_channel *pchan, + struct hab_header *header, + void *payload); + +void physical_channel_rx_dispatch(unsigned long physical_channel); + +int loopback_pchan_create(char *dev_name); + +bool hab_is_loopback(void); + +/* Global singleton HAB instance */ +extern struct hab_driver hab_driver; + +#endif /* __HAB_H */ diff --git a/drivers/soc/qcom/hab/hab_grantable.h b/drivers/soc/qcom/hab/hab_grantable.h new file mode 100644 index 000000000000..8a3f9721a89a --- /dev/null +++ b/drivers/soc/qcom/hab/hab_grantable.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __HAB_GRANTABLE_H +#define __HAB_GRANTABLE_H + +/* Grantable should be common between exporter and importer */ +struct grantable { + unsigned long pfn; +}; + +struct compressed_pfns { + unsigned long first_pfn; + int nregions; + struct region { + int size; + int space; + } region[]; +}; +#endif /* __HAB_GRANTABLE_H */ diff --git a/drivers/soc/qcom/hab/hab_mem_linux.c b/drivers/soc/qcom/hab/hab_mem_linux.c new file mode 100644 index 000000000000..ab4b9d0885cb --- /dev/null +++ b/drivers/soc/qcom/hab/hab_mem_linux.c @@ -0,0 +1,451 @@ +/* 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 "hab.h" +#include <linux/fdtable.h> +#include <linux/dma-buf.h> +#include "hab_grantable.h" + + +struct pages_list { + struct list_head list; + struct page **pages; + long npages; + uint64_t index; /* for mmap first call */ + int kernel; + void *kva; + void *uva; + int refcntk; + int refcntu; + uint32_t userflags; + struct file *filp_owner; + struct file *filp_mapper; +}; + +struct importer_context { + int cnt; /* pages allocated for local file */ + struct list_head imp_list; + struct file *filp; +}; + +void *habmm_hyp_allocate_grantable(int page_count, + uint32_t *sizebytes) +{ + if (!sizebytes || !page_count) + return NULL; + + *sizebytes = page_count * sizeof(struct grantable); + return vmalloc(*sizebytes); +} + +static int match_file(const void *p, struct file *file, unsigned int fd) +{ + /* + * We must return fd + 1 because iterate_fd stops searching on + * non-zero return, but 0 is a valid fd. + */ + return (p == file) ? (fd + 1) : 0; +} + + +static int habmem_get_dma_pages(unsigned long address, + int page_count, + struct page **pages) +{ + struct vm_area_struct *vma; + struct dma_buf *dmabuf = NULL; + unsigned long offset; + unsigned long page_offset; + struct scatterlist *s; + struct sg_table *sg_table = NULL; + struct dma_buf_attachment *attach = NULL; + struct page *page; + int i, j, rc = 0; + int fd; + + vma = find_vma(current->mm, address); + if (!vma || !vma->vm_file) + goto err; + + /* Look for the fd that matches this the vma file */ + fd = iterate_fd(current->files, 0, match_file, vma->vm_file); + if (fd == 0) { + pr_err("iterate_fd failed\n"); + goto err; + } + + offset = address - vma->vm_start; + page_offset = offset/PAGE_SIZE; + + dmabuf = dma_buf_get(fd - 1); + + attach = dma_buf_attach(dmabuf, hab_driver.dev); + if (IS_ERR_OR_NULL(attach)) { + pr_err("dma_buf_attach failed\n"); + goto err; + } + + sg_table = dma_buf_map_attachment(attach, DMA_TO_DEVICE); + + if (IS_ERR_OR_NULL(sg_table)) { + pr_err("dma_buf_map_attachment failed\n"); + goto err; + } + + for_each_sg(sg_table->sgl, s, sg_table->nents, i) { + page = sg_page(s); + + for (j = page_offset; j < (s->length >> PAGE_SHIFT); j++) { + pages[rc] = nth_page(page, j); + rc++; + if (rc >= page_count) + break; + } + if (rc >= page_count) + break; + + if (page_offset > (s->length >> PAGE_SHIFT)) { + /* carry-over the remaining offset to next s list */ + page_offset = page_offset-(s->length >> PAGE_SHIFT); + } else { + /* + * the page_offset is within this s list + * there is no more offset for the next s list + */ + page_offset = 0; + } + + } + +err: + if (!IS_ERR_OR_NULL(sg_table)) + dma_buf_unmap_attachment(attach, sg_table, DMA_TO_DEVICE); + if (!IS_ERR_OR_NULL(attach)) + dma_buf_detach(dmabuf, attach); + if (!IS_ERR_OR_NULL(dmabuf)) + dma_buf_put(dmabuf); + return rc; +} + +int habmem_hyp_grant_user(unsigned long address, + int page_count, + int flags, + int remotedom, + void *ppdata) +{ + int i, ret = 0; + struct grantable *item = (struct grantable *)ppdata; + struct page **pages; + + pages = vmalloc(page_count * sizeof(struct page *)); + if (!pages) + return -ENOMEM; + + down_read(¤t->mm->mmap_sem); + + if (HABMM_EXP_MEM_TYPE_DMA & flags) { + ret = habmem_get_dma_pages(address, + page_count, + pages); + } else { + ret = get_user_pages(current, current->mm, + address, + page_count, + 1, + 1, + pages, + NULL); + } + + if (ret > 0) { + for (i = 0; i < page_count; i++) + item[i].pfn = page_to_pfn(pages[i]); + } else { + pr_err("get %d user pages failed: %d\n", page_count, ret); + } + + vfree(pages); + up_read(¤t->mm->mmap_sem); + return ret; +} +/* + * exporter - grant & revoke + * generate shareable page list based on CPU friendly virtual "address". + * The result as an array is stored in ppdata to return to caller + * page size 4KB is assumed + */ +int habmem_hyp_grant(unsigned long address, + int page_count, + int flags, + int remotedom, + void *ppdata) +{ + int i; + struct grantable *item; + void *kva = (void *)(uintptr_t)address; + int is_vmalloc = is_vmalloc_addr(kva); + + item = (struct grantable *)ppdata; + + for (i = 0; i < page_count; i++) { + kva = (void *)(uintptr_t)(address + i*PAGE_SIZE); + if (is_vmalloc) + item[i].pfn = page_to_pfn(vmalloc_to_page(kva)); + else + item[i].pfn = page_to_pfn(virt_to_page(kva)); + } + + return 0; +} + +int habmem_hyp_revoke(void *expdata, uint32_t count) +{ + return 0; +} + +void *habmem_imp_hyp_open(void) +{ + struct importer_context *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + INIT_LIST_HEAD(&priv->imp_list); + + return priv; +} + +void habmem_imp_hyp_close(void *imp_ctx, int kernel) +{ + struct importer_context *priv = imp_ctx; + struct pages_list *pglist, *pglist_tmp; + + if (!priv) + return; + + list_for_each_entry_safe(pglist, pglist_tmp, &priv->imp_list, list) { + if (kernel && pglist->kva) + vunmap(pglist->kva); + + list_del(&pglist->list); + priv->cnt--; + + vfree(pglist->pages); + kfree(pglist); + } + + kfree(priv); +} + +/* + * setup pages, be ready for the following mmap call + * index is output to refer to this imported buffer described by the import data + */ +long habmem_imp_hyp_map(void *imp_ctx, + void *impdata, + uint32_t count, + uint32_t remotedom, + uint64_t *index, + void **pkva, + int kernel, + uint32_t userflags) +{ + struct page **pages; + struct compressed_pfns *pfn_table = impdata; + struct pages_list *pglist; + struct importer_context *priv = imp_ctx; + unsigned long pfn; + int i, j, k = 0; + + if (!pfn_table || !priv) + return -EINVAL; + + pages = vmalloc(count * sizeof(struct page *)); + if (!pages) + return -ENOMEM; + + pglist = kzalloc(sizeof(*pglist), GFP_KERNEL); + if (!pglist) { + vfree(pages); + return -ENOMEM; + } + + pfn = pfn_table->first_pfn; + for (i = 0; i < pfn_table->nregions; i++) { + for (j = 0; j < pfn_table->region[i].size; j++) { + pages[k] = pfn_to_page(pfn+j); + k++; + } + pfn += pfn_table->region[i].size + pfn_table->region[i].space; + } + + pglist->pages = pages; + pglist->npages = count; + pglist->kernel = kernel; + pglist->index = page_to_phys(pages[0]) >> PAGE_SHIFT; + pglist->refcntk = pglist->refcntu = 0; + pglist->userflags = userflags; + + *index = pglist->index << PAGE_SHIFT; + + if (kernel) { + pgprot_t prot = PAGE_KERNEL; + + if (!(userflags & HABMM_IMPORT_FLAGS_CACHED)) + prot = pgprot_writecombine(prot); + + pglist->kva = vmap(pglist->pages, pglist->npages, VM_MAP, prot); + if (pglist->kva == NULL) { + vfree(pages); + kfree(pglist); + pr_err("%ld pages vmap failed\n", pglist->npages); + return -ENOMEM; + } + + pglist->uva = NULL; + pglist->refcntk++; + *pkva = pglist->kva; + *index = (uint64_t)((uintptr_t)pglist->kva); + } else { + pglist->kva = NULL; + } + + list_add_tail(&pglist->list, &priv->imp_list); + priv->cnt++; + + return 0; +} + +/* the input index is PHY address shifted for uhab, and kva for khab */ +long habmm_imp_hyp_unmap(void *imp_ctx, + uint64_t index, + uint32_t count, + int kernel) +{ + struct importer_context *priv = imp_ctx; + struct pages_list *pglist; + int found = 0; + uint64_t pg_index = index >> PAGE_SHIFT; + + list_for_each_entry(pglist, &priv->imp_list, list) { + if (kernel) { + if (pglist->kva == (void *)((uintptr_t)index)) + found = 1; + } else { + if (pglist->index == pg_index) + found = 1; + } + + if (found) { + list_del(&pglist->list); + priv->cnt--; + break; + } + } + + if (!found) { + pr_err("failed to find export id on index %llx\n", index); + return -EINVAL; + } + + if (kernel) + if (pglist->kva) + vunmap(pglist->kva); + + vfree(pglist->pages); + kfree(pglist); + + return 0; +} + +static int hab_map_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct page *page; + struct pages_list *pglist; + + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + + /* PHY address */ + unsigned long fault_offset = + (unsigned long)vmf->virtual_address - vma->vm_start + offset; + unsigned long fault_index = fault_offset>>PAGE_SHIFT; + int page_idx; + + if (vma == NULL) + return VM_FAULT_SIGBUS; + + pglist = vma->vm_private_data; + + page_idx = fault_index - pglist->index; + if (page_idx < 0 || page_idx >= pglist->npages) { + pr_err("Out of page array. page_idx %d, pg cnt %ld", + page_idx, pglist->npages); + return VM_FAULT_SIGBUS; + } + + page = pglist->pages[page_idx]; + get_page(page); + vmf->page = page; + return 0; +} + +static void hab_map_open(struct vm_area_struct *vma) +{ +} + +static void hab_map_close(struct vm_area_struct *vma) +{ +} + +static const struct vm_operations_struct habmem_vm_ops = { + + .fault = hab_map_fault, + .open = hab_map_open, + .close = hab_map_close, +}; + +int habmem_imp_hyp_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct uhab_context *ctx = (struct uhab_context *) filp->private_data; + struct importer_context *imp_ctx = ctx->import_ctx; + long length = vma->vm_end - vma->vm_start; + struct pages_list *pglist; + int bfound = 0; + + list_for_each_entry(pglist, &imp_ctx->imp_list, list) { + if (pglist->index == vma->vm_pgoff) { + bfound = 1; + break; + } + } + + if (!bfound) { + pr_err("Failed to find pglist vm_pgoff: %d\n", vma->vm_pgoff); + return -EINVAL; + } + + if (length > pglist->npages * PAGE_SIZE) { + pr_err("Error vma length %ld not matching page list %ld\n", + length, pglist->npages * PAGE_SIZE); + return -EINVAL; + } + + vma->vm_ops = &habmem_vm_ops; + + vma->vm_private_data = pglist; + + if (!(pglist->userflags & HABMM_IMPORT_FLAGS_CACHED)) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + return 0; +} diff --git a/drivers/soc/qcom/hab/hab_mimex.c b/drivers/soc/qcom/hab/hab_mimex.c new file mode 100644 index 000000000000..aaef9aa9f414 --- /dev/null +++ b/drivers/soc/qcom/hab/hab_mimex.c @@ -0,0 +1,394 @@ +/* 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 "hab.h" +#include "hab_grantable.h" + +/* + * use physical channel to send export parcel + + * local remote + * send(export) --> IRQ store to export warehouse + * wait(export ack) <-- send(export ack) + + * the actual data consists the following 3 parts listed in order + * 1. header (uint32_t) vcid|type|size + * 2. export parcel (full struct) + * 3. full contents in export->pdata + */ + + +static int hab_export_ack_find(struct uhab_context *ctx, + struct hab_export_ack *expect_ack) +{ + int ret = 0; + struct hab_export_ack_recvd *ack_recvd; + + spin_lock_bh(&ctx->expq_lock); + + list_for_each_entry(ack_recvd, &ctx->exp_rxq, node) { + if (ack_recvd->ack.export_id == expect_ack->export_id && + ack_recvd->ack.vcid_local == expect_ack->vcid_local && + ack_recvd->ack.vcid_remote == expect_ack->vcid_remote) { + list_del(&ack_recvd->node); + kfree(ack_recvd); + ret = 1; + break; + } + ack_recvd->age++; + if (ack_recvd->age > Q_AGE_THRESHOLD) { + list_del(&ack_recvd->node); + kfree(ack_recvd); + } + } + + spin_unlock_bh(&ctx->expq_lock); + + return ret; +} + +static int hab_export_ack_wait(struct uhab_context *ctx, + struct hab_export_ack *expect_ack) +{ + int ret; + + ret = wait_event_interruptible_timeout(ctx->exp_wq, + hab_export_ack_find(ctx, expect_ack), + HZ); + if (!ret || (ret == -ERESTARTSYS)) + ret = -EAGAIN; + else if (ret > 0) + ret = 0; + return ret; +} + +/* + * Get id from free list first. if not available, new id is generated. + * Once generated it will not be erased + * assumptions: no handshake or memory map/unmap in this helper function + */ +static struct export_desc *habmem_add_export(struct virtual_channel *vchan, + int sizebytes, + uint32_t flags) +{ + struct uhab_context *ctx; + struct export_desc *exp; + + if (!vchan || !sizebytes) + return NULL; + + exp = vmalloc(sizebytes); + if (!exp) + return NULL; + + idr_preload(GFP_KERNEL); + spin_lock(&vchan->pchan->expid_lock); + exp->export_id = + idr_alloc(&vchan->pchan->expid_idr, exp, 1, 0, GFP_NOWAIT); + spin_unlock(&vchan->pchan->expid_lock); + idr_preload_end(); + + exp->readonly = flags; + exp->vchan = vchan; + exp->vcid_local = vchan->id; + exp->vcid_remote = vchan->otherend_id; + exp->domid_local = -1; /* dom id, provided on the importer */ + exp->domid_remote = vchan->pchan->dom_id; + + ctx = vchan->ctx; + write_lock(&ctx->exp_lock); + ctx->export_total++; + list_add_tail(&exp->node, &ctx->exp_whse); + write_unlock(&ctx->exp_lock); + + return exp; +} + +void habmem_remove_export(struct export_desc *exp) +{ + struct physical_channel *pchan; + struct uhab_context *ctx; + + if (!exp || !exp->vchan || !exp->vchan->ctx || !exp->vchan->pchan) + return; + + ctx = exp->vchan->ctx; + ctx->export_total--; + + pchan = exp->vchan->pchan; + + spin_lock(&pchan->expid_lock); + idr_remove(&pchan->expid_idr, exp->export_id); + spin_unlock(&pchan->expid_lock); + + vfree(exp); +} + +static int compress_pfns(void **pfns, int npages, unsigned int *data_size) +{ + int i, j = 0; + struct grantable *item = (struct grantable *)*pfns; + int region_size = 1; + struct compressed_pfns *new_table = + vmalloc(sizeof(struct compressed_pfns) + + npages * sizeof(struct region)); + + if (!new_table) + return -ENOMEM; + + new_table->first_pfn = item[0].pfn; + for (i = 1; i < npages; i++) { + if (item[i].pfn-1 == item[i-1].pfn) { + region_size++; + } else { + new_table->region[j].size = region_size; + new_table->region[j].space = item[i].pfn - + item[i-1].pfn - 1; + j++; + region_size = 1; + } + } + new_table->region[j].size = region_size; + new_table->region[j].space = 0; + new_table->nregions = j+1; + vfree(*pfns); + + *data_size = sizeof(struct compressed_pfns) + + sizeof(struct region)*new_table->nregions; + *pfns = new_table; + return 0; +} + +/* + * store the parcel to the warehouse, then send the parcel to remote side + * both exporter composed export descriptor and the grantrefids are sent + * as one msg to the importer side + */ +static int habmem_export_vchan(struct uhab_context *ctx, + struct virtual_channel *vchan, + void *pdata, + int payload_size, + int nunits, + uint32_t flags, + uint32_t *export_id) { + int ret; + struct export_desc *exp; + uint32_t sizebytes = sizeof(*exp) + payload_size; + struct hab_export_ack expected_ack = {0}; + struct hab_header header = HAB_HEADER_INITIALIZER; + + exp = habmem_add_export(vchan, sizebytes, flags); + if (!exp) + return -ENOMEM; + + /* append the pdata to the export descriptor */ + exp->payload_count = nunits; + memcpy(exp->payload, pdata, payload_size); + + HAB_HEADER_SET_SIZE(header, sizebytes); + HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_EXPORT); + HAB_HEADER_SET_ID(header, vchan->otherend_id); + ret = physical_channel_send(vchan->pchan, &header, exp); + + if (ret != 0) { + pr_err("failed to export payload to the remote %d\n", ret); + return ret; + } + + expected_ack.export_id = exp->export_id; + expected_ack.vcid_local = exp->vcid_local; + expected_ack.vcid_remote = exp->vcid_remote; + ret = hab_export_ack_wait(ctx, &expected_ack); + + *export_id = exp->export_id; + + return ret; +} + +int hab_mem_export(struct uhab_context *ctx, + struct hab_export *param, + int kernel) +{ + int ret = 0; + void *pdata_exp = NULL; + unsigned int pdata_size = 0; + uint32_t export_id = 0; + struct virtual_channel *vchan; + int page_count; + + if (!ctx || !param || param->sizebytes > HAB_MAX_EXPORT_SIZE) + return -EINVAL; + + vchan = hab_get_vchan_fromvcid(param->vcid, ctx); + if (!vchan || !vchan->pchan) { + ret = -ENODEV; + goto err; + } + + page_count = param->sizebytes/PAGE_SIZE; + pdata_exp = habmm_hyp_allocate_grantable(page_count, &pdata_size); + if (!pdata_exp) { + ret = -ENOMEM; + goto err; + } + + if (kernel) { + ret = habmem_hyp_grant((unsigned long)param->buffer, + page_count, + param->flags, + vchan->pchan->dom_id, + pdata_exp); + } else { + ret = habmem_hyp_grant_user((unsigned long)param->buffer, + page_count, + param->flags, + vchan->pchan->dom_id, + pdata_exp); + } + if (ret < 0) { + pr_err("habmem_hyp_grant failed size=%d ret=%d\n", + pdata_size, ret); + goto err; + } + + compress_pfns(&pdata_exp, page_count, &pdata_size); + + ret = habmem_export_vchan(ctx, + vchan, + pdata_exp, + pdata_size, + page_count, + param->flags, + &export_id); + + param->exportid = export_id; +err: + vfree(pdata_exp); + if (vchan) + hab_vchan_put(vchan); + return ret; +} + +int hab_mem_unexport(struct uhab_context *ctx, + struct hab_unexport *param, + int kernel) +{ + int ret = 0, found = 0; + struct export_desc *exp, *tmp; + + if (!ctx || !param) + return -EINVAL; + + write_lock(&ctx->exp_lock); + list_for_each_entry_safe(exp, tmp, &ctx->exp_whse, node) { + if ((param->exportid == exp->export_id) && + (param->vcid == exp->vcid_local)) { + list_del(&exp->node); + found = 1; + break; + } + } + write_unlock(&ctx->exp_lock); + + if (!found) + return -EINVAL; + + ret = habmem_hyp_revoke(exp->payload, exp->payload_count); + + habmem_remove_export(exp); + return ret; +} + +int hab_mem_import(struct uhab_context *ctx, + struct hab_import *param, + int kernel) +{ + int ret = 0, found = 0; + struct export_desc *exp = NULL; + + if (!ctx || !param) + return -EINVAL; + + spin_lock_bh(&ctx->imp_lock); + list_for_each_entry(exp, &ctx->imp_whse, node) { + if ((exp->export_id == param->exportid) && + (param->vcid == exp->vcid_remote)) { + found = 1; + break; + } + } + spin_unlock_bh(&ctx->imp_lock); + + if (!found) { + pr_err("Fail to get export descriptor from export id %d\n", + param->exportid); + ret = -ENODEV; + return ret; + } + + ret = habmem_imp_hyp_map(ctx->import_ctx, + exp->payload, + exp->payload_count, + exp->domid_local, + &exp->import_index, + &exp->kva, + kernel, + param->flags); + if (ret) { + pr_err("Import fail ret:%d pcnt:%d rem:%d 1st_ref:0x%X\n", + ret, exp->payload_count, + exp->domid_local, *((uint32_t *)exp->payload)); + return ret; + } + + param->index = exp->import_index; + param->kva = (uint64_t)exp->kva; + + return ret; +} + +int hab_mem_unimport(struct uhab_context *ctx, + struct hab_unimport *param, + int kernel) +{ + int ret = 0, found = 0; + struct export_desc *exp = NULL, *exp_tmp; + + if (!ctx || !param) + return -EINVAL; + + spin_lock_bh(&ctx->imp_lock); + list_for_each_entry_safe(exp, exp_tmp, &ctx->imp_whse, node) { + if ((exp->export_id == param->exportid) && + (param->vcid == exp->vcid_remote)) { + list_del(&exp->node); + ctx->import_total--; + found = 1; + break; + } + } + spin_unlock_bh(&ctx->imp_lock); + + if (!found) + ret = -EINVAL; + else { + ret = habmm_imp_hyp_unmap(ctx->import_ctx, + exp->import_index, + exp->payload_count, + kernel); + + param->kva = (uint64_t)exp->kva; + kfree(exp); + } + + return ret; +} diff --git a/drivers/soc/qcom/hab/hab_msg.c b/drivers/soc/qcom/hab/hab_msg.c new file mode 100644 index 000000000000..f08cc83fe9fc --- /dev/null +++ b/drivers/soc/qcom/hab/hab_msg.c @@ -0,0 +1,208 @@ +/* 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 "hab.h" + +static int hab_rx_queue_empty(struct virtual_channel *vchan) +{ + int ret; + + spin_lock_bh(&vchan->rx_lock); + ret = list_empty(&vchan->rx_list); + spin_unlock_bh(&vchan->rx_lock); + return ret; +} + +static struct hab_message* +hab_msg_alloc(struct physical_channel *pchan, size_t sizebytes) +{ + struct hab_message *message; + + message = kzalloc(sizeof(*message) + sizebytes, GFP_ATOMIC); + if (!message) + return NULL; + + message->sizebytes = + physical_channel_read(pchan, message->data, sizebytes); + + return message; +} + +void hab_msg_free(struct hab_message *message) +{ + kfree(message); +} + +struct hab_message * +hab_msg_dequeue(struct virtual_channel *vchan, int wait_flag) +{ + struct hab_message *message = NULL; + int ret = 0; + + if (wait_flag) { + if (hab_rx_queue_empty(vchan)) + ret = wait_event_interruptible(vchan->rx_queue, + !hab_rx_queue_empty(vchan) || + vchan->otherend_closed); + } + + if (!ret && !vchan->otherend_closed) { + spin_lock_bh(&vchan->rx_lock); + if (!list_empty(&vchan->rx_list)) { + message = list_first_entry(&vchan->rx_list, + struct hab_message, node); + list_del(&message->node); + } + spin_unlock_bh(&vchan->rx_lock); + } + + return message; +} + +static void hab_msg_queue(struct virtual_channel *vchan, + struct hab_message *message) +{ + spin_lock_bh(&vchan->rx_lock); + list_add_tail(&message->node, &vchan->rx_list); + spin_unlock_bh(&vchan->rx_lock); + + wake_up_interruptible(&vchan->rx_queue); +} + +static int hab_export_enqueue(struct virtual_channel *vchan, + struct export_desc *exp) +{ + struct uhab_context *ctx = vchan->ctx; + + spin_lock_bh(&ctx->imp_lock); + list_add_tail(&exp->node, &ctx->imp_whse); + ctx->import_total++; + spin_unlock_bh(&ctx->imp_lock); + + return 0; +} + +static int hab_send_export_ack(struct physical_channel *pchan, + struct export_desc *exp) +{ + struct hab_export_ack exp_ack = { + .export_id = exp->export_id, + .vcid_local = exp->vcid_local, + .vcid_remote = exp->vcid_remote + }; + struct hab_header header = HAB_HEADER_INITIALIZER; + + HAB_HEADER_SET_SIZE(header, sizeof(exp_ack)); + HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_EXPORT_ACK); + HAB_HEADER_SET_ID(header, exp->vcid_local); + return physical_channel_send(pchan, &header, &exp_ack); +} + +static int hab_receive_create_export_ack(struct physical_channel *pchan, + struct uhab_context *ctx) +{ + struct hab_export_ack_recvd *ack_recvd = + kzalloc(sizeof(*ack_recvd), GFP_ATOMIC); + + if (!ack_recvd) + return -ENOMEM; + + if (physical_channel_read(pchan, + &ack_recvd->ack, + sizeof(ack_recvd->ack)) != sizeof(ack_recvd->ack)) + return -EIO; + + spin_lock_bh(&ctx->expq_lock); + list_add_tail(&ack_recvd->node, &ctx->exp_rxq); + spin_unlock_bh(&ctx->expq_lock); + + return 0; +} + +void hab_msg_recv(struct physical_channel *pchan, + struct hab_header *header) +{ + int ret; + struct hab_message *message; + struct hab_device *dev = pchan->habdev; + size_t sizebytes = HAB_HEADER_GET_SIZE(*header); + uint32_t payload_type = HAB_HEADER_GET_TYPE(*header); + uint32_t vchan_id = HAB_HEADER_GET_ID(*header); + struct virtual_channel *vchan = NULL; + struct export_desc *exp_desc; + + /* get the local virtual channel if it isn't an open message */ + if (payload_type != HAB_PAYLOAD_TYPE_INIT && + payload_type != HAB_PAYLOAD_TYPE_INIT_ACK && + payload_type != HAB_PAYLOAD_TYPE_ACK) { + vchan = hab_vchan_get(pchan, vchan_id); + if (!vchan) { + return; + } else if (vchan->otherend_closed) { + hab_vchan_put(vchan); + return; + } + } + + switch (payload_type) { + case HAB_PAYLOAD_TYPE_MSG: + message = hab_msg_alloc(pchan, sizebytes); + if (!message) + break; + + hab_msg_queue(vchan, message); + break; + + case HAB_PAYLOAD_TYPE_INIT: + case HAB_PAYLOAD_TYPE_INIT_ACK: + case HAB_PAYLOAD_TYPE_ACK: + ret = hab_open_request_add(pchan, header); + if (ret) + break; + wake_up_interruptible(&dev->openq); + break; + + case HAB_PAYLOAD_TYPE_EXPORT: + exp_desc = kzalloc(sizebytes, GFP_ATOMIC); + if (!exp_desc) + break; + + if (physical_channel_read(pchan, exp_desc, sizebytes) != + sizebytes) { + vfree(exp_desc); + break; + } + + exp_desc->domid_local = pchan->dom_id; + + hab_export_enqueue(vchan, exp_desc); + hab_send_export_ack(pchan, exp_desc); + break; + + case HAB_PAYLOAD_TYPE_EXPORT_ACK: + ret = hab_receive_create_export_ack(pchan, vchan->ctx); + if (ret) + break; + + wake_up_interruptible(&vchan->ctx->exp_wq); + break; + + case HAB_PAYLOAD_TYPE_CLOSE: + hab_vchan_stop(vchan); + break; + + default: + break; + } + if (vchan) + hab_vchan_put(vchan); +} diff --git a/drivers/soc/qcom/hab/hab_open.c b/drivers/soc/qcom/hab/hab_open.c new file mode 100644 index 000000000000..66468aa43afd --- /dev/null +++ b/drivers/soc/qcom/hab/hab_open.c @@ -0,0 +1,154 @@ +/* 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 "hab.h" + +void hab_open_request_init(struct hab_open_request *request, + int type, + struct physical_channel *pchan, + int vchan_id, + int sub_id, + int open_id) +{ + request->type = type; + request->pchan = pchan; + request->vchan_id = vchan_id; + request->sub_id = sub_id; + request->open_id = open_id; +} + +int hab_open_request_send(struct hab_open_request *request) +{ + struct hab_header header = HAB_HEADER_INITIALIZER; + struct hab_open_send_data data; + + HAB_HEADER_SET_SIZE(header, sizeof(struct hab_open_send_data)); + HAB_HEADER_SET_TYPE(header, request->type); + + data.vchan_id = request->vchan_id; + data.open_id = request->open_id; + data.sub_id = request->sub_id; + + return physical_channel_send(request->pchan, &header, &data); +} + +int hab_open_request_add(struct physical_channel *pchan, + struct hab_header *header) +{ + struct hab_open_node *node; + struct hab_device *dev = pchan->habdev; + struct hab_open_send_data data; + struct hab_open_request *request; + + node = kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + return -ENOMEM; + + if (physical_channel_read(pchan, &data, HAB_HEADER_GET_SIZE(*header)) != + HAB_HEADER_GET_SIZE(*header)) + return -EIO; + + request = &node->request; + request->type = HAB_HEADER_GET_TYPE(*header); + request->pchan = pchan; + request->vchan_id = data.vchan_id; + request->sub_id = data.sub_id; + request->open_id = data.open_id; + node->age = 0; + hab_pchan_get(pchan); + + spin_lock_bh(&dev->openlock); + list_add_tail(&node->node, &dev->openq_list); + spin_unlock_bh(&dev->openlock); + + return 0; +} + +static int hab_open_request_find(struct uhab_context *ctx, + struct hab_device *dev, + struct hab_open_request *listen, + struct hab_open_request **recv_request) +{ + struct hab_open_node *node, *tmp; + struct hab_open_request *request; + int ret = 0; + + if (ctx->closing || + (listen->pchan && listen->pchan->closed)) { + *recv_request = NULL; + return 1; + } + + spin_lock_bh(&dev->openlock); + if (list_empty(&dev->openq_list)) + goto done; + + list_for_each_entry_safe(node, tmp, &dev->openq_list, node) { + request = (struct hab_open_request *)node; + if (request->type == listen->type && + (request->sub_id == listen->sub_id) && + (!listen->open_id || + request->open_id == listen->open_id) && + (!listen->pchan || + request->pchan == listen->pchan)) { + list_del(&node->node); + *recv_request = request; + ret = 1; + break; + } + node->age++; + if (node->age > Q_AGE_THRESHOLD) { + list_del(&node->node); + hab_open_request_free(request); + } + } + +done: + spin_unlock_bh(&dev->openlock); + return ret; +} + +void hab_open_request_free(struct hab_open_request *request) +{ + if (request) { + hab_pchan_put(request->pchan); + kfree(request); + } +} + +int hab_open_listen(struct uhab_context *ctx, + struct hab_device *dev, + struct hab_open_request *listen, + struct hab_open_request **recv_request, + int ms_timeout) +{ + int ret = 0; + + if (!ctx || !listen || !recv_request) + return -EINVAL; + + *recv_request = NULL; + if (ms_timeout > 0) { + ret = wait_event_interruptible_timeout(dev->openq, + hab_open_request_find(ctx, dev, listen, recv_request), + ms_timeout); + if (!ret || (-ERESTARTSYS == ret)) + ret = -EAGAIN; + else if (ret > 0) + ret = 0; + } else { + ret = wait_event_interruptible(dev->openq, + hab_open_request_find(ctx, dev, listen, recv_request)); + } + + return ret; +} diff --git a/drivers/soc/qcom/hab/hab_pchan.c b/drivers/soc/qcom/hab/hab_pchan.c new file mode 100644 index 000000000000..1ad727f7d90f --- /dev/null +++ b/drivers/soc/qcom/hab/hab_pchan.c @@ -0,0 +1,86 @@ +/* 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 "hab.h" + +struct physical_channel * +hab_pchan_alloc(struct hab_device *habdev, int otherend_id) +{ + struct physical_channel *pchan = kzalloc(sizeof(*pchan), GFP_KERNEL); + + if (!pchan) + return NULL; + + idr_init(&pchan->vchan_idr); + spin_lock_init(&pchan->vid_lock); + idr_init(&pchan->expid_idr); + spin_lock_init(&pchan->expid_lock); + kref_init(&pchan->refcount); + + pchan->habdev = habdev; + pchan->dom_id = otherend_id; + pchan->closed = 1; + pchan->hyp_data = NULL; + + spin_lock_init(&pchan->rxbuf_lock); + + mutex_lock(&habdev->pchan_lock); + list_add_tail(&pchan->node, &habdev->pchannels); + mutex_unlock(&habdev->pchan_lock); + + return pchan; +} + +static void hab_pchan_free(struct kref *ref) +{ + struct physical_channel *pchan = + container_of(ref, struct physical_channel, refcount); + + mutex_lock(&pchan->habdev->pchan_lock); + list_del(&pchan->node); + mutex_unlock(&pchan->habdev->pchan_lock); + kfree(pchan->hyp_data); + kfree(pchan); +} + +struct physical_channel * +hab_pchan_find_domid(struct hab_device *dev, int dom_id) +{ + struct physical_channel *pchan; + + mutex_lock(&dev->pchan_lock); + list_for_each_entry(pchan, &dev->pchannels, node) + if (pchan->dom_id == dom_id) + break; + + if (pchan->dom_id != dom_id) + pchan = NULL; + + if (pchan && !kref_get_unless_zero(&pchan->refcount)) + pchan = NULL; + + mutex_unlock(&dev->pchan_lock); + + return pchan; +} + +void hab_pchan_get(struct physical_channel *pchan) +{ + if (pchan) + kref_get(&pchan->refcount); +} + +void hab_pchan_put(struct physical_channel *pchan) +{ + if (pchan) + kref_put(&pchan->refcount, hab_pchan_free); +} diff --git a/drivers/soc/qcom/hab/hab_pipe.c b/drivers/soc/qcom/hab/hab_pipe.c new file mode 100644 index 000000000000..e757b6cb1f01 --- /dev/null +++ b/drivers/soc/qcom/hab/hab_pipe.c @@ -0,0 +1,131 @@ +/* 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 "hab.h" +#include "hab_pipe.h" + +size_t hab_pipe_calc_required_bytes(uint32_t shared_buf_size) +{ + return sizeof(struct hab_pipe) + + (2 * (sizeof(struct hab_shared_buf) + shared_buf_size)); +} + +struct hab_pipe_endpoint *hab_pipe_init(struct hab_pipe *pipe, + uint32_t shared_buf_size, int top) +{ + struct hab_pipe_endpoint *ep = NULL; + struct hab_shared_buf *buf_a; + struct hab_shared_buf *buf_b; + + if (!pipe) + return NULL; + + buf_a = (struct hab_shared_buf *) pipe->buf_base; + buf_b = (struct hab_shared_buf *) (pipe->buf_base + + sizeof(struct hab_shared_buf) + shared_buf_size); + + if (top) { + ep = &pipe->top; + memset(ep, 0, sizeof(*ep)); + ep->tx_info.sh_buf = buf_a; + ep->rx_info.sh_buf = buf_b; + } else { + ep = &pipe->bottom; + memset(ep, 0, sizeof(*ep)); + ep->tx_info.sh_buf = buf_b; + ep->rx_info.sh_buf = buf_a; + memset(ep->tx_info.sh_buf, 0, sizeof(struct hab_shared_buf)); + memset(ep->rx_info.sh_buf, 0, sizeof(struct hab_shared_buf)); + ep->tx_info.sh_buf->size = shared_buf_size; + ep->rx_info.sh_buf->size = shared_buf_size; + + pipe->buf_a = buf_a; + pipe->buf_b = buf_b; + pipe->total_size = + hab_pipe_calc_required_bytes(shared_buf_size); + } + return ep; +} + +uint32_t hab_pipe_write(struct hab_pipe_endpoint *ep, + unsigned char *p, uint32_t num_bytes) +{ + struct hab_shared_buf *sh_buf = ep->tx_info.sh_buf; + uint32_t space = + (sh_buf->size - (ep->tx_info.wr_count - sh_buf->rd_count)); + uint32_t count1, count2; + + if (!p || num_bytes > space || num_bytes == 0) + return 0; + + count1 = (num_bytes <= (sh_buf->size - ep->tx_info.index)) ? num_bytes : + (sh_buf->size - ep->tx_info.index); + count2 = num_bytes - count1; + + if (count1 > 0) { + memcpy(&sh_buf->data[ep->tx_info.index], p, count1); + ep->tx_info.wr_count += count1; + ep->tx_info.index += count1; + if (ep->tx_info.index >= sh_buf->size) + ep->tx_info.index = 0; + } + if (count2 > 0) {/* handle buffer wrapping */ + memcpy(&sh_buf->data[ep->tx_info.index], p + count1, count2); + ep->tx_info.wr_count += count2; + ep->tx_info.index += count2; + if (ep->tx_info.index >= sh_buf->size) + ep->tx_info.index = 0; + } + return num_bytes; +} + +/* Updates the write index which is shared with the other VM */ +void hab_pipe_write_commit(struct hab_pipe_endpoint *ep) +{ + struct hab_shared_buf *sh_buf = ep->tx_info.sh_buf; + + mb(); /* Must commit data before incrementing count */ + sh_buf->wr_count = ep->tx_info.wr_count; +} + +uint32_t hab_pipe_read(struct hab_pipe_endpoint *ep, + unsigned char *p, uint32_t size) +{ + struct hab_shared_buf *sh_buf = ep->rx_info.sh_buf; + uint32_t avail = sh_buf->wr_count - sh_buf->rd_count; + uint32_t count1, count2, to_read; + + if (!p || avail == 0 || size == 0) + return 0; + + to_read = (avail < size) ? avail : size; + count1 = (to_read <= (sh_buf->size - ep->rx_info.index)) ? to_read : + (sh_buf->size - ep->rx_info.index); + count2 = to_read - count1; + + if (count1 > 0) { + memcpy(p, &sh_buf->data[ep->rx_info.index], count1); + ep->rx_info.index += count1; + if (ep->rx_info.index >= sh_buf->size) + ep->rx_info.index = 0; + mb(); /*Must commit data before incremeting count*/ + sh_buf->rd_count += count1; + } + if (count2 > 0) { /* handle buffer wrapping */ + memcpy(p + count1, &sh_buf->data[ep->rx_info.index], count2); + ep->rx_info.index += count2; + mb(); /*Must commit data before incremeting count*/ + sh_buf->rd_count += count2; + } + + return to_read; +} diff --git a/drivers/soc/qcom/hab/hab_pipe.h b/drivers/soc/qcom/hab/hab_pipe.h new file mode 100644 index 000000000000..6ffdbd133868 --- /dev/null +++ b/drivers/soc/qcom/hab/hab_pipe.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef HAB_PIPE_H +#define HAB_PIPE_H + +struct hab_shared_buf { + uint32_t rd_count; + uint32_t wr_count; + uint32_t size; + unsigned char data[]; +}; + +struct hab_pipe_endpoint { + struct { + uint32_t wr_count; + uint32_t index; + struct hab_shared_buf *sh_buf; + } tx_info; + struct { + uint32_t index; + struct hab_shared_buf *sh_buf; + } rx_info; +}; + +struct hab_pipe { + struct hab_pipe_endpoint top; + struct hab_pipe_endpoint bottom; + + /* For debugging only */ + struct hab_shared_buf *buf_a; /* top TX, bottom RX */ + struct hab_shared_buf *buf_b; /* top RX, bottom TX */ + size_t total_size; + + unsigned char buf_base[]; +}; + +size_t hab_pipe_calc_required_bytes(uint32_t shared_buf_size); + +struct hab_pipe_endpoint *hab_pipe_init(struct hab_pipe *pipe, + uint32_t shared_buf_size, int top); + +uint32_t hab_pipe_write(struct hab_pipe_endpoint *ep, + unsigned char *p, uint32_t num_bytes); + +void hab_pipe_write_commit(struct hab_pipe_endpoint *ep); + +uint32_t hab_pipe_read(struct hab_pipe_endpoint *ep, + unsigned char *p, uint32_t size); + +#endif /* HAB_PIPE_H */ diff --git a/drivers/soc/qcom/hab/hab_qvm.c b/drivers/soc/qcom/hab/hab_qvm.c new file mode 100644 index 000000000000..a37590f23c61 --- /dev/null +++ b/drivers/soc/qcom/hab/hab_qvm.c @@ -0,0 +1,251 @@ +/* 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 "hab.h" +#include "hab_qvm.h" + +#include <linux/highmem.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/io.h> + +#include <linux/of.h> +#include <linux/of_platform.h> + +#define DEFAULT_HAB_SHMEM_IRQ 7 + +#define SHMEM_PHYSICAL_ADDR 0x1c050000 + +static irqreturn_t shm_irq_handler(int irq, void *_pchan) +{ + irqreturn_t rc = IRQ_NONE; + struct physical_channel *pchan = _pchan; + struct qvm_channel *dev = + (struct qvm_channel *) (pchan ? pchan->hyp_data : NULL); + + if (dev && dev->guest_ctrl) { + int status = dev->guest_ctrl->status; + + if (status & dev->idx) { + rc = IRQ_HANDLED; + tasklet_schedule(&dev->task); + } + } + return rc; +} + +static uint64_t get_guest_factory_paddr(struct qvm_channel *dev, + const char *name, uint32_t pages) +{ + int i; + + dev->guest_factory = ioremap(SHMEM_PHYSICAL_ADDR, PAGE_SIZE); + + if (!dev->guest_factory) { + pr_err("Couldn't map guest_factory\n"); + return 0; + } + + if (dev->guest_factory->signature != GUEST_SHM_SIGNATURE) { + pr_err("shmem factory signature incorrect: %ld != %lu\n", + GUEST_SHM_SIGNATURE, dev->guest_factory->signature); + iounmap(dev->guest_factory); + return 0; + } + + dev->guest_intr = dev->guest_factory->vector; + + /* + * Set the name field on the factory page to identify the shared memory + * region + */ + for (i = 0; i < strlen(name) && i < GUEST_SHM_MAX_NAME - 1; i++) + dev->guest_factory->name[i] = name[i]; + dev->guest_factory->name[i] = (char) 0; + + guest_shm_create(dev->guest_factory, pages); + + /* See if we successfully created/attached to the region. */ + if (dev->guest_factory->status != GSS_OK) { + pr_err("create failed: %d\n", dev->guest_factory->status); + iounmap(dev->guest_factory); + return 0; + } + + pr_debug("shm creation size %x\n", dev->guest_factory->size); + + return dev->guest_factory->shmem; +} + +static int create_dispatcher(struct physical_channel *pchan, int id) +{ + struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data; + int ret; + + tasklet_init(&dev->task, physical_channel_rx_dispatch, + (unsigned long) pchan); + + ret = request_irq(hab_driver.irq, shm_irq_handler, IRQF_SHARED, + hab_driver.devp[id].name, pchan); + + if (ret) + pr_err("request_irq for %s failed: %d\n", + hab_driver.devp[id].name, ret); + + return ret; +} + +static struct physical_channel *habhyp_commdev_alloc(int id) +{ + struct qvm_channel *dev; + struct physical_channel *pchan = NULL; + int ret = 0, channel = 0; + char *shmdata; + uint32_t pipe_alloc_size = + hab_pipe_calc_required_bytes(PIPE_SHMEM_SIZE); + uint32_t pipe_alloc_pages = + (pipe_alloc_size + PAGE_SIZE - 1) / PAGE_SIZE; + uint64_t paddr; + int temp; + int total_pages; + struct page **pages; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&dev->io_lock); + + paddr = get_guest_factory_paddr(dev, + hab_driver.devp[id].name, + pipe_alloc_pages); + + total_pages = dev->guest_factory->size + 1; + pages = kmalloc_array(total_pages, sizeof(struct page *), GFP_KERNEL); + if (!pages) { + ret = -ENOMEM; + goto err; + } + + for (temp = 0; temp < total_pages; temp++) + pages[temp] = pfn_to_page((paddr / PAGE_SIZE) + temp); + + dev->guest_ctrl = vmap(pages, total_pages, VM_MAP, PAGE_KERNEL); + if (!dev->guest_ctrl) { + ret = -ENOMEM; + kfree(pages); + goto err; + } + + shmdata = (char *)dev->guest_ctrl + PAGE_SIZE; + dev->idx = dev->guest_ctrl->idx; + + kfree(pages); + + dev->pipe = (struct hab_pipe *) shmdata; + dev->pipe_ep = hab_pipe_init(dev->pipe, PIPE_SHMEM_SIZE, + dev->be ? 0 : 1); + + pchan = hab_pchan_alloc(&hab_driver.devp[id], dev->be); + if (!pchan) { + ret = -ENOMEM; + goto err; + } + + pchan->closed = 0; + pchan->hyp_data = (void *)dev; + + dev->channel = channel; + + ret = create_dispatcher(pchan, id); + if (ret < 0) + goto err; + + return pchan; + +err: + kfree(dev); + + if (pchan) + hab_pchan_put(pchan); + pr_err("habhyp_commdev_alloc failed: %d\n", ret); + return ERR_PTR(ret); +} + +int hab_hypervisor_register(void) +{ + int ret = 0, i; + + hab_driver.b_server_dom = 0; + + /* + * Can still attempt to instantiate more channels if one fails. + * Others can be retried later. + */ + for (i = 0; i < hab_driver.ndevices; i++) { + if (IS_ERR(habhyp_commdev_alloc(i))) + ret = -EAGAIN; + } + + return ret; +} + +void hab_hypervisor_unregister(void) +{ +} + +static int hab_shmem_probe(struct platform_device *pdev) +{ + int irq = platform_get_irq(pdev, 0); + + if (irq > 0) + hab_driver.irq = irq; + else + hab_driver.irq = DEFAULT_HAB_SHMEM_IRQ; + + return 0; +} + +static int hab_shmem_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id hab_shmem_match_table[] = { + {.compatible = "qvm,guest_shm"}, + {}, +}; + +static struct platform_driver hab_shmem_driver = { + .probe = hab_shmem_probe, + .remove = hab_shmem_remove, + .driver = { + .name = "hab_shmem", + .of_match_table = of_match_ptr(hab_shmem_match_table), + }, +}; + +static int __init hab_shmem_init(void) +{ + return platform_driver_register(&hab_shmem_driver); +} + +static void __exit hab_shmem_exit(void) +{ + platform_driver_unregister(&hab_shmem_driver); +} + +core_initcall(hab_shmem_init); +module_exit(hab_shmem_exit); + +MODULE_DESCRIPTION("Hypervisor shared memory driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/hab/hab_qvm.h b/drivers/soc/qcom/hab/hab_qvm.h new file mode 100644 index 000000000000..e94b82f87942 --- /dev/null +++ b/drivers/soc/qcom/hab/hab_qvm.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __HAB_QNX_H +#define __HAB_QNX_H +#include "hab.h" +#include "hab_pipe.h" + +#include <guest_shm.h> +#include <linux/stddef.h> + +#define PULSE_CODE_NOTIFY 0 +#define PULSE_CODE_INPUT 1 + +struct qvm_channel { + int be; + + struct hab_pipe *pipe; + struct hab_pipe_endpoint *pipe_ep; + spinlock_t io_lock; + struct tasklet_struct task; + struct guest_shm_factory *guest_factory; + struct guest_shm_control *guest_ctrl; + uint32_t idx; + + int channel; + int coid; + + unsigned int guest_intr; + unsigned int guest_iid; +}; + +/* Shared mem size in each direction for communication pipe */ +#define PIPE_SHMEM_SIZE (128 * 1024) + +void *qnx_hyp_rx_dispatch(void *data); + +#endif /* __HAB_QNX_H */ diff --git a/drivers/soc/qcom/hab/hab_vchan.c b/drivers/soc/qcom/hab/hab_vchan.c new file mode 100644 index 000000000000..75a3fad68ab5 --- /dev/null +++ b/drivers/soc/qcom/hab/hab_vchan.c @@ -0,0 +1,184 @@ +/* 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 "hab.h" + +struct virtual_channel * +hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan) +{ + int id; + struct virtual_channel *vchan; + + if (!pchan || !ctx) + return NULL; + + vchan = kzalloc(sizeof(*vchan), GFP_KERNEL); + if (!vchan) + return NULL; + + /* This should be the first thing we do in this function */ + idr_preload(GFP_KERNEL); + spin_lock_bh(&pchan->vid_lock); + id = idr_alloc(&pchan->vchan_idr, vchan, 1, 256, GFP_NOWAIT); + spin_unlock_bh(&pchan->vid_lock); + idr_preload_end(); + + if (id < 0) { + kfree(vchan); + return NULL; + } + mb(); /* id must be generated done before pchan_get */ + + hab_pchan_get(pchan); + vchan->pchan = pchan; + vchan->id = ((id << HAB_VCID_ID_SHIFT) & HAB_VCID_ID_MASK) | + ((pchan->habdev->id << HAB_VCID_MMID_SHIFT) & + HAB_VCID_MMID_MASK) | + ((pchan->dom_id << HAB_VCID_DOMID_SHIFT) & + HAB_VCID_DOMID_MASK); + spin_lock_init(&vchan->rx_lock); + INIT_LIST_HEAD(&vchan->rx_list); + init_waitqueue_head(&vchan->rx_queue); + + kref_init(&vchan->refcount); + kref_init(&vchan->usagecnt); + vchan->otherend_closed = pchan->closed; + + hab_ctx_get(ctx); + vchan->ctx = ctx; + + return vchan; +} + +static void +hab_vchan_free(struct kref *ref) +{ + int found; + struct virtual_channel *vchan = + container_of(ref, struct virtual_channel, refcount); + struct hab_message *message, *msg_tmp; + struct export_desc *exp; + struct physical_channel *pchan = vchan->pchan; + struct uhab_context *ctx = vchan->ctx; + + list_for_each_entry_safe(message, msg_tmp, &vchan->rx_list, node) { + list_del(&message->node); + hab_msg_free(message); + } + + do { + found = 0; + write_lock(&ctx->exp_lock); + list_for_each_entry(exp, &ctx->exp_whse, node) { + if (exp->vcid_local == vchan->id) { + list_del(&exp->node); + found = 1; + break; + } + } + write_unlock(&ctx->exp_lock); + if (found) { + habmem_hyp_revoke(exp->payload, exp->payload_count); + habmem_remove_export(exp); + } + } while (found); + + do { + found = 0; + spin_lock_bh(&ctx->imp_lock); + list_for_each_entry(exp, &ctx->imp_whse, node) { + if (exp->vcid_remote == vchan->id) { + list_del(&exp->node); + found = 1; + break; + } + } + spin_unlock_bh(&ctx->imp_lock); + if (found) { + habmm_imp_hyp_unmap(ctx->import_ctx, + exp->import_index, + exp->payload_count, + ctx->kernel); + ctx->import_total--; + kfree(exp); + } + } while (found); + + spin_lock_bh(&pchan->vid_lock); + idr_remove(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan->id)); + spin_unlock_bh(&pchan->vid_lock); + + hab_pchan_put(pchan); + hab_ctx_put(ctx); + + kfree(vchan); +} + +struct virtual_channel* +hab_vchan_get(struct physical_channel *pchan, uint32_t vchan_id) +{ + struct virtual_channel *vchan; + + spin_lock_bh(&pchan->vid_lock); + vchan = idr_find(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan_id)); + if (vchan) + if (!kref_get_unless_zero(&vchan->refcount)) + vchan = NULL; + spin_unlock_bh(&pchan->vid_lock); + + return vchan; +} + +void hab_vchan_stop(struct virtual_channel *vchan) +{ + if (vchan) { + vchan->otherend_closed = 1; + wake_up_interruptible(&vchan->rx_queue); + } +} + +void hab_vchan_stop_notify(struct virtual_channel *vchan) +{ + hab_send_close_msg(vchan); + hab_vchan_stop(vchan); +} + + +int hab_vchan_find_domid(struct virtual_channel *vchan) +{ + return vchan ? vchan->pchan->dom_id : -1; +} + +static void +hab_vchan_free_deferred(struct work_struct *work) +{ + struct virtual_channel *vchan = + container_of(work, struct virtual_channel, work); + + hab_vchan_free(&vchan->refcount); +} + +static void +hab_vchan_schedule_free(struct kref *ref) +{ + struct virtual_channel *vchan = + container_of(ref, struct virtual_channel, refcount); + + INIT_WORK(&vchan->work, hab_vchan_free_deferred); + schedule_work(&vchan->work); +} + +void hab_vchan_put(struct virtual_channel *vchan) +{ + if (vchan) + kref_put(&vchan->refcount, hab_vchan_schedule_free); +} diff --git a/drivers/soc/qcom/hab/khab.c b/drivers/soc/qcom/hab/khab.c new file mode 100644 index 000000000000..f7499773ae42 --- /dev/null +++ b/drivers/soc/qcom/hab/khab.c @@ -0,0 +1,140 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/module.h> +#include "hab.h" + +int32_t habmm_socket_open(int32_t *handle, uint32_t mm_ip_id, + uint32_t timeout, uint32_t flags) +{ + return hab_vchan_open(hab_driver.kctx, mm_ip_id, handle, flags); +} +EXPORT_SYMBOL(habmm_socket_open); + +int32_t habmm_socket_close(int32_t handle) +{ + hab_vchan_close(hab_driver.kctx, handle); + return 0; +} +EXPORT_SYMBOL(habmm_socket_close); + +int32_t habmm_socket_send(int32_t handle, void *src_buff, + uint32_t size_bytes, uint32_t flags) +{ + struct hab_send param = {0}; + + param.vcid = handle; + param.data = (uint64_t)(uintptr_t)src_buff; + param.sizebytes = size_bytes; + param.flags = flags; + + return hab_vchan_send(hab_driver.kctx, handle, + size_bytes, src_buff, flags); +} +EXPORT_SYMBOL(habmm_socket_send); + +int32_t habmm_socket_recv(int32_t handle, void *dst_buff, uint32_t *size_bytes, + uint32_t timeout, uint32_t flags) +{ + int ret = 0; + struct hab_message *msg; + + if (!size_bytes || !dst_buff) + return -EINVAL; + + msg = hab_vchan_recv(hab_driver.kctx, handle, flags); + + if (IS_ERR(msg)) { + *size_bytes = 0; + return PTR_ERR(msg); + } + + if (*size_bytes < msg->sizebytes) { + *size_bytes = 0; + ret = -EINVAL; + } else { + memcpy(dst_buff, msg->data, msg->sizebytes); + *size_bytes = msg->sizebytes; + } + + hab_msg_free(msg); + return ret; +} +EXPORT_SYMBOL(habmm_socket_recv); + +int32_t habmm_export(int32_t handle, void *buff_to_share, uint32_t size_bytes, + uint32_t *export_id, uint32_t flags) +{ + int ret; + struct hab_export param = {0}; + + if (!export_id) + return -EINVAL; + + param.vcid = handle; + param.buffer = (uint64_t)(uintptr_t)buff_to_share; + param.sizebytes = size_bytes; + + ret = hab_mem_export(hab_driver.kctx, ¶m, 1); + + *export_id = param.exportid; + return ret; +} +EXPORT_SYMBOL(habmm_export); + +int32_t habmm_unexport(int32_t handle, uint32_t export_id, uint32_t flags) +{ + struct hab_unexport param = {0}; + + param.vcid = handle; + param.exportid = export_id; + + return hab_mem_unexport(hab_driver.kctx, ¶m, 1); +} +EXPORT_SYMBOL(habmm_unexport); + +int32_t habmm_import(int32_t handle, void **buff_shared, uint32_t size_bytes, + uint32_t export_id, uint32_t flags) +{ + int ret; + struct hab_import param = {0}; + + if (!buff_shared) + return -EINVAL; + + param.vcid = handle; + param.sizebytes = size_bytes; + param.exportid = export_id; + param.flags = flags; + + ret = hab_mem_import(hab_driver.kctx, ¶m, 1); + if (!IS_ERR(ret)) + *buff_shared = (void *)(uintptr_t)param.kva; + + return ret; +} +EXPORT_SYMBOL(habmm_import); + +int32_t habmm_unimport(int32_t handle, + uint32_t export_id, + void *buff_shared, + uint32_t flags) +{ + struct hab_unimport param = {0}; + + param.vcid = handle; + param.exportid = export_id; + param.kva = (uint64_t)(uintptr_t)buff_shared; + + return hab_mem_unimport(hab_driver.kctx, ¶m, 1); +} +EXPORT_SYMBOL(habmm_unimport); diff --git a/drivers/soc/qcom/hab/qvm_comm.c b/drivers/soc/qcom/hab/qvm_comm.c new file mode 100644 index 000000000000..20a631e13794 --- /dev/null +++ b/drivers/soc/qcom/hab/qvm_comm.c @@ -0,0 +1,95 @@ +/* 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 "hab.h" +#include "hab_qvm.h" + +static inline void habhyp_notify(void *commdev) +{ + struct qvm_channel *dev = (struct qvm_channel *)commdev; + + if (dev && dev->guest_ctrl) + dev->guest_ctrl->notify = ~0; +} + +int physical_channel_read(struct physical_channel *pchan, + void *payload, + size_t read_size) +{ + struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data; + + if (dev) + return hab_pipe_read(dev->pipe_ep, payload, read_size); + else + return 0; +} + +int physical_channel_send(struct physical_channel *pchan, + struct hab_header *header, + void *payload) +{ + int sizebytes = HAB_HEADER_GET_SIZE(*header); + struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data; + int total_size = sizeof(*header) + sizebytes; + + if (total_size > dev->pipe_ep->tx_info.sh_buf->size) + return -EINVAL; /* too much data for ring */ + + spin_lock_bh(&dev->io_lock); + + if ((dev->pipe_ep->tx_info.sh_buf->size - + (dev->pipe_ep->tx_info.wr_count - + dev->pipe_ep->tx_info.sh_buf->rd_count)) < total_size) { + spin_unlock_bh(&dev->io_lock); + return -EAGAIN; /* not enough free space */ + } + + if (hab_pipe_write(dev->pipe_ep, + (unsigned char *)header, + sizeof(*header)) != sizeof(*header)) { + spin_unlock_bh(&dev->io_lock); + return -EIO; + } + + if (sizebytes) { + if (hab_pipe_write(dev->pipe_ep, + (unsigned char *)payload, + sizebytes) != sizebytes) { + spin_unlock_bh(&dev->io_lock); + return -EIO; + } + } + + hab_pipe_write_commit(dev->pipe_ep); + spin_unlock_bh(&dev->io_lock); + habhyp_notify(dev); + + return 0; +} + +void physical_channel_rx_dispatch(unsigned long data) +{ + struct hab_header header; + struct physical_channel *pchan = (struct physical_channel *)data; + struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data; + + spin_lock_bh(&pchan->rxbuf_lock); + while (1) { + if (hab_pipe_read(dev->pipe_ep, + (unsigned char *)&header, + sizeof(header)) != sizeof(header)) + break; /* no data available */ + + hab_msg_recv(pchan, &header); + } + spin_unlock_bh(&pchan->rxbuf_lock); +} diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 7f2e4cc42e51..2326487302fd 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -294,6 +294,7 @@ enum icnss_driver_state { ICNSS_WLFW_EXISTS, ICNSS_SHUTDOWN_DONE, ICNSS_HOST_TRIGGERED_PDR, + ICNSS_FW_DOWN, }; struct ce_irq_list { @@ -504,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; @@ -1950,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, @@ -1996,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) { @@ -2496,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 @@ -2623,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); @@ -2631,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; } @@ -3824,6 +3837,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) 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/ipc_router_glink_xprt.c b/drivers/soc/qcom/ipc_router_glink_xprt.c index 7dd1683881fb..0c588c586306 100644 --- a/drivers/soc/qcom/ipc_router_glink_xprt.c +++ b/drivers/soc/qcom/ipc_router_glink_xprt.c @@ -82,6 +82,7 @@ struct ipc_router_glink_xprt { struct msm_ipc_router_xprt xprt; void *ch_hndl; struct workqueue_struct *xprt_wq; + struct wakeup_source notify_rxv_ws; struct rw_semaphore ss_reset_rwlock; int ss_reset; void *pil; @@ -377,6 +378,7 @@ out_read_data: glink_rx_done(glink_xprtp->ch_hndl, rx_work->iovec, reuse_intent); kfree(rx_work); up_read(&glink_xprtp->ss_reset_rwlock); + __pm_relax(&glink_xprtp->notify_rxv_ws); } static void glink_xprt_open_event(struct work_struct *work) @@ -491,6 +493,8 @@ static void glink_xprt_notify_rxv(void *handle, const void *priv, rx_work->iovec_size = size; rx_work->vbuf_provider = vbuf_provider; rx_work->pbuf_provider = pbuf_provider; + if (!glink_xprtp->dynamic_wakeup_source) + __pm_stay_awake(&glink_xprtp->notify_rxv_ws); INIT_WORK(&rx_work->work, glink_xprt_read_data); queue_work(glink_xprtp->xprt_wq, &rx_work->work); } @@ -760,6 +764,7 @@ static int ipc_router_glink_config_init( return -EFAULT; } + wakeup_source_init(&glink_xprtp->notify_rxv_ws, xprt_wq_name); mutex_lock(&glink_xprt_list_lock_lha1); list_add(&glink_xprtp->list, &glink_xprt_list); mutex_unlock(&glink_xprt_list_lock_lha1); 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/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 b119c7a8441d..9e61ff1ebfcc 100644 --- a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c +++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c @@ -210,11 +210,44 @@ done: return ret; } -int msm_audio_ion_phys_assign(const char *name, int fd, ion_phys_addr_t *paddr, +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) { - struct ion_client *client; - struct ion_handle *handle; int ret; if (!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) { @@ -222,40 +255,43 @@ int msm_audio_ion_phys_assign(const char *name, int fd, ion_phys_addr_t *paddr, return -EPROBE_DEFER; } - if (!name || !paddr || !pa_len) { + 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))) { + *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))) { + *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); + 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; } - pr_debug("%s: ION Physical address is %x\n", __func__, (u32)*paddr); ret = msm_audio_hyp_assign(paddr, pa_len, assign_type); + return ret; + err_ion_handle: - ion_free(client, handle); + ion_free(*client, *handle); err_destroy_client: - ion_client_destroy(client); + ion_client_destroy(*client); + *client = NULL; + *handle = NULL; return ret; } 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_qcpe.c b/drivers/soc/qcom/scm_qcpe.c index 1e369c73e34b..3f2b05a0ec9e 100644 --- a/drivers/soc/qcom/scm_qcpe.c +++ b/drivers/soc/qcom/scm_qcpe.c @@ -1027,55 +1027,56 @@ 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; - - return desc.ret[0]; + if (!ret && desc.ret[0]) + return 1; + else + return 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/smp2p_spinlock_test.c b/drivers/soc/qcom/smp2p_spinlock_test.c index 74aac52b5285..1fe4411eebde 100644 --- a/drivers/soc/qcom/smp2p_spinlock_test.c +++ b/drivers/soc/qcom/smp2p_spinlock_test.c @@ -1,6 +1,6 @@ /* drivers/soc/qcom/smp2p_spinlock_test.c * - * 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 @@ -522,7 +522,7 @@ static void smp2p_ut_remote_spinlock_ssr(struct seq_file *s) int spinlock_owner = 0; struct workqueue_struct *ws = NULL; - struct rmt_spinlock_work_item work_item; + struct rmt_spinlock_work_item work_item = { .has_locked = false }; seq_printf(s, " Running %s Test\n", __func__); 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 24de162e5401..a49848808078 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -209,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; @@ -739,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; @@ -752,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); @@ -780,7 +779,7 @@ 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); @@ -1139,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; } @@ -1326,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; } @@ -2029,6 +2030,7 @@ static int spcom_handle_get_req_size(struct spcom_channel *ch, void *buf, uint32_t size) { + int ret = -1; uint32_t next_req_size = 0; if (size < sizeof(next_req_size)) { @@ -2036,7 +2038,10 @@ static int spcom_handle_get_req_size(struct spcom_channel *ch, return -EINVAL; } - next_req_size = spcom_get_next_request_size(ch); + ret = spcom_get_next_request_size(ch); + if (ret < 0) + return ret; + next_req_size = (uint32_t) ret; memcpy(buf, &next_req_size, sizeof(next_req_size)); pr_debug("next_req_size [%d].\n", next_req_size); @@ -2141,18 +2146,20 @@ static int spcom_handle_read(struct spcom_channel *ch, void *buf, uint32_t size) { + int ret = -1; + if (size == SPCOM_GET_NEXT_REQUEST_SIZE) { pr_debug("get next request size, ch [%s].\n", ch->name); ch->is_server = true; - size = spcom_handle_get_req_size(ch, buf, size); + ret = spcom_handle_get_req_size(ch, buf, size); } else { pr_debug("get request/response, ch [%s].\n", ch->name); - size = spcom_handle_read_req_resp(ch, buf, size); + ret = spcom_handle_read_req_resp(ch, buf, size); } pr_debug("ch [%s] , size = %d.\n", ch->name, size); - return size; + return ret; } /*======================================================================*/ @@ -2304,6 +2311,7 @@ static ssize_t spcom_device_write(struct file *filp, char *buf; struct spcom_channel *ch; const char *name = file_to_filename(filp); + int buf_size = 0; pr_debug("Write file [%s] size [%d] pos [%d].\n", name, (int) size, (int) *f_pos); @@ -2330,6 +2338,7 @@ static ssize_t spcom_device_write(struct file *filp, (int) size , (int) SPCOM_MAX_COMMAND_SIZE); return -EINVAL; } + buf_size = size; /* explicit casting size_t to int */ if (*f_pos != 0) { pr_err("offset should be zero, no sparse buffer.\n"); @@ -2347,7 +2356,7 @@ static ssize_t spcom_device_write(struct file *filp, return -EFAULT; } - ret = spcom_handle_write(ch, buf, size); + ret = spcom_handle_write(ch, buf, buf_size); if (ret) { pr_err("handle command error [%d].\n", ret); kfree(buf); @@ -2375,6 +2384,7 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, char *buf; struct spcom_channel *ch; const char *name = file_to_filename(filp); + uint32_t buf_size = 0; pr_debug("Read file [%s], size = %d bytes.\n", name, (int) size); @@ -2383,6 +2393,7 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, pr_err("invalid parameters.\n"); return -EINVAL; } + buf_size = size; /* explicit casting size_t to uint32_t */ ch = filp->private_data; @@ -2400,7 +2411,7 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, if (buf == NULL) return -ENOMEM; - ret = spcom_handle_read(ch, buf, size); + ret = spcom_handle_read(ch, buf, buf_size); if (ret < 0) { pr_err("read error [%d].\n", ret); kfree(buf); @@ -2483,9 +2494,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) { @@ -2786,7 +2802,7 @@ static int __init spcom_init(void) { int ret; - pr_info("spcom driver version 1.1 17-July-2017.\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/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c index 5fe3c572628b..85c2b92f5474 100644 --- a/drivers/soc/qcom/wcd-dsp-glink.c +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -570,7 +570,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, mutex_lock(&wpriv->glink_mutex); if (wpriv->ch) { - dev_err(wpriv->dev, "%s: glink ch memory is already allocated\n", + dev_err_ratelimited(wpriv->dev, "%s: glink ch memory is already allocated\n", __func__); ret = -EINVAL; goto done; @@ -579,7 +579,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, no_of_channels = pkt->no_of_channels; if (no_of_channels > WDSP_MAX_NO_OF_CHANNELS) { - dev_err(wpriv->dev, "%s: no_of_channels: %d but max allowed are %d\n", + dev_err_ratelimited(wpriv->dev, "%s: no_of_channels: %d but max allowed are %d\n", __func__, no_of_channels, WDSP_MAX_NO_OF_CHANNELS); ret = -EINVAL; goto done; @@ -598,20 +598,20 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, size += WDSP_CH_CFG_SIZE; if (size > pkt_size) { - dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", __func__, size, pkt_size); ret = -EINVAL; goto err_ch_mem; } if (ch_cfg->no_of_intents > WDSP_MAX_NO_OF_INTENTS) { - dev_err(wpriv->dev, "%s: Invalid no_of_intents = %d\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid no_of_intents = %d\n", __func__, ch_cfg->no_of_intents); ret = -EINVAL; goto err_ch_mem; } size += (sizeof(u32) * ch_cfg->no_of_intents); if (size > pkt_size) { - dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", __func__, size, pkt_size); ret = -EINVAL; goto err_ch_mem; @@ -746,7 +746,7 @@ static ssize_t wdsp_glink_read(struct file *file, char __user *buf, } if (count > WDSP_MAX_READ_SIZE) { - dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", + dev_info_ratelimited(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", __func__, count); count = WDSP_MAX_READ_SIZE; } @@ -778,7 +778,7 @@ static ssize_t wdsp_glink_read(struct file *file, char __user *buf, if (ret1) { mutex_unlock(&wpriv->rsp_mutex); - dev_err(wpriv->dev, "%s: copy_to_user failed %d\n", + dev_err_ratelimited(wpriv->dev, "%s: copy_to_user failed %d\n", __func__, ret); ret = -EFAULT; goto done; @@ -824,7 +824,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, if ((count < WDSP_WRITE_PKT_SIZE) || (count > WDSP_MAX_WRITE_SIZE)) { - dev_err(wpriv->dev, "%s: Invalid count = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid count = %zd\n", __func__, count); ret = -EINVAL; goto done; @@ -841,7 +841,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, ret = copy_from_user(tx_buf->buf, buf, count); if (ret) { - dev_err(wpriv->dev, "%s: copy_from_user failed %d\n", + dev_err_ratelimited(wpriv->dev, "%s: copy_from_user failed %d\n", __func__, ret); ret = -EFAULT; goto free_buf; @@ -852,7 +852,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, case WDSP_REG_PKT: if (count < (WDSP_WRITE_PKT_SIZE + WDSP_REG_PKT_SIZE + WDSP_CH_CFG_SIZE)) { - dev_err(wpriv->dev, "%s: Invalid reg pkt size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid reg pkt size = %zd\n", __func__, count); ret = -EINVAL; goto free_buf; @@ -861,7 +861,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, (struct wdsp_reg_pkt *)wpkt->payload, count); if (IS_ERR_VALUE(ret)) - dev_err(wpriv->dev, "%s: glink register failed, ret = %d\n", + dev_err_ratelimited(wpriv->dev, "%s: glink register failed, ret = %d\n", __func__, ret); vfree(tx_buf); break; @@ -871,7 +871,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, GLINK_LINK_STATE_UP), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { - dev_err(wpriv->dev, "%s: Link state wait timeout\n", + dev_err_ratelimited(wpriv->dev, "%s: Link state wait timeout\n", __func__); ret = -ETIMEDOUT; goto free_buf; @@ -881,7 +881,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, break; case WDSP_CMD_PKT: if (count <= (WDSP_WRITE_PKT_SIZE + WDSP_CMD_PKT_SIZE)) { - dev_err(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n", __func__, count); ret = -EINVAL; goto free_buf; @@ -889,7 +889,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, mutex_lock(&wpriv->glink_mutex); if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) { mutex_unlock(&wpriv->glink_mutex); - dev_err(wpriv->dev, "%s: Link state is Down\n", + dev_err_ratelimited(wpriv->dev, "%s: Link state is Down\n", __func__); ret = -ENETRESET; @@ -901,7 +901,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, sizeof(struct wdsp_cmd_pkt) + cpkt->payload_size; if (count < pkt_max_size) { - dev_err(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n", __func__, count, pkt_max_size); ret = -EINVAL; goto free_buf; @@ -917,7 +917,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, } } if (!tx_buf->ch) { - dev_err(wpriv->dev, "%s: Failed to get glink channel\n", + dev_err_ratelimited(wpriv->dev, "%s: Failed to get glink channel\n", __func__); ret = -EINVAL; goto free_buf; @@ -928,7 +928,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, GLINK_CONNECTED), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { - dev_err(wpriv->dev, "%s: glink channel %s is not in connected state %d\n", + dev_err_ratelimited(wpriv->dev, "%s: glink channel %s is not in connected state %d\n", __func__, tx_buf->ch->ch_cfg.name, tx_buf->ch->channel_state); ret = -ETIMEDOUT; @@ -940,7 +940,8 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, queue_work(wpriv->work_queue, &tx_buf->tx_work); break; default: - dev_err(wpriv->dev, "%s: Invalid packet type\n", __func__); + dev_err_ratelimited(wpriv->dev, "%s: Invalid packet type\n", + __func__); ret = -EINVAL; vfree(tx_buf); break; @@ -986,6 +987,7 @@ static int wdsp_glink_open(struct inode *inode, struct file *file) goto err_wq; } + wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN; init_completion(&wpriv->rsp_complete); init_waitqueue_head(&wpriv->link_state_wait); mutex_init(&wpriv->rsp_mutex); 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/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index 4b586f62cdc7..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", @@ -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/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c2eba06f2ace..0744b14e120b 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1333,8 +1333,6 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_event_buffers_cleanup(dwc); dwc3_free_event_buffers(dwc); - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); phy_power_off(dwc->usb2_generic_phy); phy_power_off(dwc->usb3_generic_phy); diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index c5fc77eae894..5ad68df298cd 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1239,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; @@ -1252,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) { @@ -1933,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"); 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/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index a98e5a9007bd..be4d1ec1589b 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1637,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; } diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 786fe10055da..c0632e8241a0 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -684,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_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 2143c2bdb84b..a49c5290753c 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -2322,6 +2322,13 @@ int hdmi_edid_parser(void *input) goto bail; } + /* Find out if CEA extension blocks exceeding max limit */ + if (num_of_cea_blocks >= MAX_EDID_BLOCKS) { + DEV_WARN("%s: HDMI EDID exceeded max CEA blocks limit\n", + __func__); + num_of_cea_blocks = MAX_EDID_BLOCKS - 1; + } + /* check for valid CEA block */ if (edid_buf[EDID_BLOCK_SIZE] != 2) { DEV_ERR("%s: Invalid CEA block\n", __func__); diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index ff93c343d41f..b07ba82fde34 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -3191,11 +3191,14 @@ int mdss_mdp_layer_atomic_validate_wfd(struct msm_fb_data_type *mfd, goto validate_failed; } + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); rc = mdss_mdp_wfd_setup(wfd, output_layer); if (rc) { pr_err("fail to prepare wfd = %d\n", rc); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); goto validate_failed; } + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); rc = mdss_mdp_layer_atomic_validate(mfd, file, commit); if (rc) { diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h index fc481037478a..f3422440c45f 100644 --- a/include/linux/dma-iommu.h +++ b/include/linux/dma-iommu.h @@ -59,7 +59,6 @@ void iommu_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs); void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs); -int iommu_dma_supported(struct device *dev, u64 mask); int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr); #else diff --git a/include/linux/habmm.h b/include/linux/habmm.h new file mode 100644 index 000000000000..4d3a81f536d9 --- /dev/null +++ b/include/linux/habmm.h @@ -0,0 +1,38 @@ +/* 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 <uapi/linux/habmm.h> + +#ifndef _HABMM_H +#define _HABMM_H + +int32_t habmm_socket_open(int32_t *handle, uint32_t mm_ip_id, + uint32_t timeout, uint32_t flags); +int32_t habmm_socket_close(int32_t handle); +int32_t habmm_socket_send(int32_t handle, void *src_buff, uint32_t size_bytes, + uint32_t flags); +int32_t habmm_socket_recv(int32_t handle, void *dst_buff, uint32_t *size_bytes, + uint32_t timeout, uint32_t flags); +int32_t habmm_socket_sendto(int32_t handle, void *src_buff, uint32_t size_bytes, + int32_t remote_handle, uint32_t flags); +int32_t habmm_socket_recvfrom(int32_t handle, void *dst_buff, + uint32_t *size_bytes, uint32_t timeout, + int32_t *remote_handle, uint32_t flags); +int32_t habmm_export(int32_t handle, void *buff_to_share, uint32_t size_bytes, + uint32_t *export_id, uint32_t flags); +int32_t habmm_unexport(int32_t handle, uint32_t export_id, uint32_t flags); +int32_t habmm_import(int32_t handle, void **buff_shared, uint32_t size_bytes, + uint32_t export_id, uint32_t flags); +int32_t habmm_unimport(int32_t handle, uint32_t export_id, void *buff_shared, + uint32_t flags); + +#endif diff --git a/include/linux/msm_audio_ion.h b/include/linux/msm_audio_ion.h index ff2fd04a3b6c..9e77ac317c28 100644 --- a/include/linux/msm_audio_ion.h +++ b/include/linux/msm_audio_ion.h @@ -50,6 +50,12 @@ 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, int fd, ion_phys_addr_t *paddr, - size_t *pa_len, u8 assign_type); +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/power_supply.h b/include/linux/power_supply.h index 982b93ccfbe4..8b8a46ce32d0 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -199,6 +199,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_LOW_POWER, POWER_SUPPLY_PROP_COOL_TEMP, POWER_SUPPLY_PROP_WARM_TEMP, + POWER_SUPPLY_PROP_COLD_TEMP, + POWER_SUPPLY_PROP_HOT_TEMP, POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, POWER_SUPPLY_PROP_RESISTANCE, POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE, diff --git a/include/linux/sched.h b/include/linux/sched.h index 2fe0b38f5572..d1542e5cc81b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2501,6 +2501,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) @@ -2513,6 +2514,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/soc/qcom/minidump.h b/include/soc/qcom/minidump.h index 5eb18cb1a365..d5970cfef643 100644 --- a/include/soc/qcom/minidump.h +++ b/include/soc/qcom/minidump.h @@ -39,11 +39,16 @@ struct md_region { extern int msm_minidump_add_region(const struct md_region *entry); /* Sets to true, if minidump table is initialized */ extern bool minidump_enabled; +extern void dump_stack_minidump(u64 sp); #else static inline int msm_minidump_add_region(const struct md_region *entry) { /* Return quietly, if minidump is not supported */ return 0; } + +static inline void dump_stack_minidump(u64 sp) {} #endif + + #endif 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/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/q6asm-v2.h b/include/sound/q6asm-v2.h index 2ff0a2b02d4a..9ddd02cac9ac 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -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_audio_map_shm_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/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/Kbuild b/include/uapi/linux/Kbuild index 3d912dd57c08..b041334338f9 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -528,3 +528,4 @@ header-y += ipa_qmi_service_v01.h header-y += rmnet_ipa_fd_ioctl.h header-y += msm_ipa.h header-y += smcinvoke.h +header-y += habmm.h diff --git a/include/uapi/linux/habmm.h b/include/uapi/linux/habmm.h new file mode 100644 index 000000000000..902bd35ee474 --- /dev/null +++ b/include/uapi/linux/habmm.h @@ -0,0 +1,143 @@ +#ifndef HABMM_H +#define HABMM_H + +#include <linux/types.h> + +struct hab_send { + __u64 data; + __s32 vcid; + __u32 sizebytes; + __u32 flags; +}; + +struct hab_recv { + __u64 data; + __s32 vcid; + __u32 sizebytes; + __u32 flags; +}; + +struct hab_open { + __s32 vcid; + __u32 mmid; + __u32 timeout; + __u32 flags; +}; + +struct hab_close { + __s32 vcid; + __u32 flags; +}; + +struct hab_export { + __u64 buffer; + __s32 vcid; + __u32 sizebytes; + __u32 exportid; + __u32 flags; +}; + +struct hab_import { + __u64 index; + __u64 kva; + __s32 vcid; + __u32 sizebytes; + __u32 exportid; + __u32 flags; +}; + +struct hab_unexport { + __s32 vcid; + __u32 exportid; + __u32 flags; +}; + +struct hab_unimport { + __s32 vcid; + __u32 exportid; + __u64 kva; + __u32 flags; +}; + +#define HAB_IOC_TYPE 0x0A +#define HAB_MAX_MSG_SIZEBYTES 0x1000 +#define HAB_MAX_EXPORT_SIZE 0x8000000 + +#define HAB_MMID_CREATE(major, minor) ((major&0xFFFF) | ((minor&0xFF)<<16)) + +#define MM_AUD_START 100 +#define MM_AUD_1 101 +#define MM_AUD_2 102 +#define MM_AUD_3 103 +#define MM_AUD_4 104 +#define MM_AUD_END 105 + +#define MM_CAM_START 200 +#define MM_CAM 201 +#define MM_CAM_END 202 + +#define MM_DISP_START 300 +#define MM_DISP_1 301 +#define MM_DISP_2 302 +#define MM_DISP_3 303 +#define MM_DISP_4 304 +#define MM_DISP_5 305 +#define MM_DISP_END 306 + +#define MM_GFX_START 400 +#define MM_GFX 401 +#define MM_GFX_END 402 + +#define MM_VID_START 500 +#define MM_VID 501 +#define MM_VID_END 502 + +#define MM_MISC_START 600 +#define MM_MISC 601 +#define MM_MISC_END 602 + +#define MM_QCPE_START 700 +#define MM_QCPE_VM1 701 +#define MM_QCPE_VM2 702 +#define MM_QCPE_VM3 703 +#define MM_QCPE_VM4 704 +#define MM_QCPE_END 705 +#define MM_ID_MAX 706 + +#define HABMM_SOCKET_OPEN_FLAGS_SINGLE_BE_SINGLE_FE 0x00000000 +#define HABMM_SOCKET_OPEN_FLAGS_SINGLE_BE_SINGLE_DOMU 0x00000001 +#define HABMM_SOCKET_OPEN_FLAGS_SINGLE_BE_MULTI_DOMUS 0x00000002 + +#define HABMM_SOCKET_SEND_FLAGS_NON_BLOCKING 0x00000001 + +#define HABMM_SOCKET_RECV_FLAGS_NON_BLOCKING 0x00000001 + +#define HABMM_EXP_MEM_TYPE_DMA 0x00000001 + +#define HABMM_IMPORT_FLAGS_CACHED 0x00000001 + +#define IOCTL_HAB_SEND \ + _IOW(HAB_IOC_TYPE, 0x2, struct hab_send) + +#define IOCTL_HAB_RECV \ + _IOWR(HAB_IOC_TYPE, 0x3, struct hab_recv) + +#define IOCTL_HAB_VC_OPEN \ + _IOWR(HAB_IOC_TYPE, 0x4, struct hab_open) + +#define IOCTL_HAB_VC_CLOSE \ + _IOW(HAB_IOC_TYPE, 0x5, struct hab_close) + +#define IOCTL_HAB_VC_EXPORT \ + _IOWR(HAB_IOC_TYPE, 0x6, struct hab_export) + +#define IOCTL_HAB_VC_IMPORT \ + _IOWR(HAB_IOC_TYPE, 0x7, struct hab_import) + +#define IOCTL_HAB_VC_UNEXPORT \ + _IOW(HAB_IOC_TYPE, 0x8, struct hab_unexport) + +#define IOCTL_HAB_VC_UNIMPORT \ + _IOW(HAB_IOC_TYPE, 0x9, struct hab_unimport) + +#endif /* HABMM_H */ diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h index 4d0b992d0ba6..322fb09b8614 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -91,7 +91,11 @@ #define IPA_IOCTL_ALLOC_IPV6CT_TABLE 49 #define IPA_IOCTL_DEL_NAT_TABLE 50 #define IPA_IOCTL_DEL_IPV6CT_TABLE 51 -#define IPA_IOCTL_MAX 52 +#define IPA_IOCTL_ADD_VLAN_IFACE 52 +#define IPA_IOCTL_DEL_VLAN_IFACE 53 +#define IPA_IOCTL_ADD_L2TP_VLAN_MAPPING 54 +#define IPA_IOCTL_DEL_L2TP_VLAN_MAPPING 55 +#define IPA_IOCTL_MAX 56 /** * max size of the header to be inserted @@ -435,7 +439,16 @@ enum ipa_ssr_event { IPA_SSR_EVENT_MAX }; -#define IPA_EVENT_MAX_NUM ((int)IPA_SSR_EVENT_MAX) +enum ipa_vlan_l2tp_event { + ADD_VLAN_IFACE = IPA_SSR_EVENT_MAX, + DEL_VLAN_IFACE, + ADD_L2TP_VLAN_MAPPING, + DEL_L2TP_VLAN_MAPPING, + IPA_VLAN_L2TP_EVENT_MAX, +}; + +#define IPA_EVENT_MAX_NUM (IPA_VLAN_L2TP_EVENT_MAX) +#define IPA_EVENT_MAX ((int)IPA_EVENT_MAX_NUM) /** * enum ipa_rm_resource_name - IPA RM clients identification names @@ -1448,6 +1461,30 @@ struct ipa_ioc_nat_pdn_entry { }; /** + * struct ipa_ioc_vlan_iface_info - add vlan interface + * @name: interface name + * @vlan_id: VLAN ID + */ +struct ipa_ioc_vlan_iface_info { + char name[IPA_RESOURCE_NAME_MAX]; + uint8_t vlan_id; +}; + +/** + * struct ipa_ioc_l2tp_vlan_mapping_info - l2tp->vlan mapping info + * @iptype: l2tp tunnel IP type + * @l2tp_iface_name: l2tp interface name + * @l2tp_session_id: l2tp session id + * @vlan_iface_name: vlan interface name + */ +struct ipa_ioc_l2tp_vlan_mapping_info { + enum ipa_ip_type iptype; + char l2tp_iface_name[IPA_RESOURCE_NAME_MAX]; + uint8_t l2tp_session_id; + char vlan_iface_name[IPA_RESOURCE_NAME_MAX]; +}; + +/** * struct ipa_msg_meta - Format of the message meta-data. * @msg_type: the type of the message * @rsvd: reserved bits for future use. @@ -1742,6 +1779,21 @@ enum ipacm_client_enum { IPA_IOCTL_GET_HW_VERSION, \ enum ipa_hw_type *) +#define IPA_IOC_ADD_VLAN_IFACE _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_ADD_VLAN_IFACE, \ + struct ipa_ioc_vlan_iface_info *) + +#define IPA_IOC_DEL_VLAN_IFACE _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_DEL_VLAN_IFACE, \ + struct ipa_ioc_vlan_iface_info *) + +#define IPA_IOC_ADD_L2TP_VLAN_MAPPING _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_ADD_L2TP_VLAN_MAPPING, \ + struct ipa_ioc_l2tp_vlan_mapping_info *) + +#define IPA_IOC_DEL_L2TP_VLAN_MAPPING _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_DEL_L2TP_VLAN_MAPPING, \ + struct ipa_ioc_l2tp_vlan_mapping_info *) /* * unique magic number of the Tethering bridge ioctls */ diff --git a/include/uapi/media/ais/msm_ais_sensor.h b/include/uapi/media/ais/msm_ais_sensor.h index f8b98def850a..eb9c24024383 100644 --- a/include/uapi/media/ais/msm_ais_sensor.h +++ b/include/uapi/media/ais/msm_ais_sensor.h @@ -178,6 +178,27 @@ enum cci_i2c_master_t { MASTER_MAX, }; +struct msm_sensor_event_data { + uint16_t sensor_slave_addr; +}; + +enum msm_sensor_event_mask_index { + SENSOR_EVENT_MASK_INDEX_SIGNAL_STATUS = 2, +}; + +#define SENSOR_EVENT_SUBS_MASK_NONE 0 + +#define SENSOR_EVENT_SUBS_MASK_SIGNAL_STATUS \ + (1 << SENSOR_EVENT_MASK_INDEX_SIGNAL_STATUS) + +enum msm_sensor_event_idx { + SENSOR_SIGNAL_STATUS = 2, + SENSOR_EVENT_MAX = 15 +}; + +#define SENSOR_EVENT_BASE (V4L2_EVENT_PRIVATE_START) +#define SENSOR_EVENT_SIGNAL_STATUS (SENSOR_EVENT_BASE + SENSOR_SIGNAL_STATUS) + struct msm_camera_i2c_array_write_config { struct msm_camera_i2c_reg_setting conf_array; uint16_t slave_addr; diff --git a/include/uapi/media/ais/msm_ais_sensor_sdk.h b/include/uapi/media/ais/msm_ais_sensor_sdk.h index c2a93a51a985..3f63bde39cf3 100644 --- a/include/uapi/media/ais/msm_ais_sensor_sdk.h +++ b/include/uapi/media/ais/msm_ais_sensor_sdk.h @@ -285,6 +285,11 @@ struct msm_sensor_id_info_t { unsigned short sensor_id_mask; }; +struct msm_camera_sensor_gpio_intr_config { + int gpio_num; + uint32_t gpio_trigger; +}; + struct msm_camera_sensor_slave_info { char sensor_name[32]; char eeprom_name[32]; @@ -300,6 +305,9 @@ struct msm_camera_sensor_slave_info { unsigned char is_init_params_valid; struct msm_sensor_init_params sensor_init_params; enum msm_sensor_output_format_t output_format; + struct msm_camera_sensor_gpio_intr_config + gpio_intr_config; + unsigned int camera_sensor_device_id; }; struct msm_camera_i2c_reg_array { diff --git a/kernel/panic.c b/kernel/panic.c index 679254405510..75f564a94a82 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -24,6 +24,7 @@ #include <linux/init.h> #include <linux/nmi.h> #include <linux/console.h> +#include <soc/qcom/minidump.h> #define CREATE_TRACE_POINTS #include <trace/events/exception.h> @@ -108,6 +109,7 @@ void panic(const char *fmt, ...) va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); + dump_stack_minidump(0); pr_emerg("Kernel panic - not syncing: %s\n", buf); #ifdef CONFIG_DEBUG_BUGVERBOSE /* 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/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/lib/stackdepot.c b/lib/stackdepot.c index 192134b225ca..076eb03e316b 100644 --- a/lib/stackdepot.c +++ b/lib/stackdepot.c @@ -242,6 +242,7 @@ depot_stack_handle_t depot_save_stack(struct stack_trace *trace, */ alloc_flags &= ~GFP_ZONEMASK; alloc_flags &= (GFP_ATOMIC | GFP_KERNEL); + alloc_flags |= __GFP_NOWARN; page = alloc_pages(alloc_flags, STACK_ALLOC_ORDER); if (page) prealloc = page_address(page); 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/wsa881x.c b/sound/soc/codecs/wsa881x.c index 058f6c5fa676..e4f6df077d98 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -968,6 +968,8 @@ static void wsa881x_init(struct snd_soc_codec *codec) wsa881x->version = snd_soc_read(codec, WSA881X_CHIP_ID1); wsa881x_regmap_defaults(wsa881x->regmap, wsa881x->version); + /* Enable software reset output from soundwire slave */ + snd_soc_update_bits(codec, WSA881X_SWR_RESET_EN, 0x07, 0x07); /* Bring out of analog reset */ snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x02, 0x02); /* Bring out of digital reset */ diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig index eb650d19a97c..629a9c3d91db 100644 --- a/sound/soc/msm/Kconfig +++ b/sound/soc/msm/Kconfig @@ -166,7 +166,7 @@ config SND_SOC_EXT_CODEC config SND_SOC_MSM8996 tristate "SoC Machine driver for MSM8996 boards" - depends on ARCH_MSM8996 + depends on ARCH_MSM8996 || MSM_GVM_QUIN select SND_SOC_COMPRESS select SND_SOC_QDSP6V2 select SND_SOC_MSM_STUB @@ -176,7 +176,7 @@ config SND_SOC_MSM8996 select MSM_QDSP6V2_CODECS select SND_SOC_WCD9335 select SND_SOC_WSA881X - select SND_SOC_MSM_HDMI_CODEC_RX + select SND_SOC_MSM_HDMI_CODEC_RX if ARCH_MSM8996 select DTS_SRS_TM select QTI_PP select QTI_PP_AUDIOSPHERE diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 076dbed207a9..c462f682e160 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -159,6 +159,10 @@ struct msm_compr_audio { 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; @@ -1510,20 +1514,16 @@ static int msm_compr_configure_dsp_for_capture(struct snd_compr_stream *cstream) return ret; } -static int msm_compr_map_unmap_fd(int fd, bool add_pages) +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; - u8 assign_type; - if (add_pages) - assign_type = HLOS_TO_ADSP; - else - assign_type = ADSP_TO_HLOS; - - ret = msm_audio_ion_phys_assign("audio_lib_mem_client", fd, - &paddr, &pa_len, assign_type); + 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); @@ -1531,19 +1531,48 @@ static int msm_compr_map_unmap_fd(int fd, bool add_pages) } ret = q6core_add_remove_pool_pages(paddr, pa_len, - ADSP_MEMORY_MAP_HLOS_PHYSPOOL, add_pages); + 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 */ - if (add_pages) - msm_audio_ion_phys_assign("audio_lib_mem_client", fd, - &paddr, &pa_len, ADSP_TO_HLOS); + 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; @@ -1628,8 +1657,8 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) 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_unmap_fd( - pdata->ion_fd[rtd->dai_link->be_id], true); + ret = msm_compr_map_ion_fd(prtd, + pdata->ion_fd[rtd->dai_link->be_id]); if (ret < 0) goto map_err; } @@ -1800,12 +1829,11 @@ 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_assign("audio_shm_mem_client", - prtd->shm_ion_fd, - &paddr, &pa_len, ADSP_TO_HLOS); + 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_map_unmap_fd(pdata->ion_fd[soc_prtd->dai_link->be_id], - false); + msm_compr_unmap_ion_fd(prtd); pdata->ion_fd[soc_prtd->dai_link->be_id] = 0; } @@ -3715,6 +3743,119 @@ done: return ret; } +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) { @@ -3755,7 +3896,9 @@ static int msm_compr_shm_ion_fd_map_put(struct snd_kcontrol *kcontrol, 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_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 map shm mem\n", __func__); done: @@ -3984,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"; @@ -4282,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"; @@ -4585,6 +4785,11 @@ static int msm_compr_new(struct snd_soc_pcm_runtime *rtd) 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-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 7326e658c947..2114adae72d7 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -12211,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), @@ -12223,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", @@ -15485,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"}, @@ -15524,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" }, 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 fdeb8a15ffee..18cac3424054 100644 --- a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c @@ -92,6 +92,10 @@ 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 */ @@ -192,20 +196,17 @@ static void populate_codec_list(struct msm_transcode_loopback *trans, } } -static int msm_transcode_map_unmap_fd(int fd, bool add_pages) +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; - u8 assign_type; - if (add_pages) - assign_type = HLOS_TO_ADSP; - else - assign_type = ADSP_TO_HLOS; - - ret = msm_audio_ion_phys_assign("audio_lib_mem_client", fd, - &paddr, &pa_len, assign_type); + 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); @@ -213,19 +214,47 @@ static int msm_transcode_map_unmap_fd(int fd, bool add_pages) } ret = q6core_add_remove_pool_pages(paddr, pa_len, - ADSP_MEMORY_MAP_HLOS_PHYSPOOL, add_pages); + ADSP_MEMORY_MAP_HLOS_PHYSPOOL, true); if (ret) { - pr_err("%s: add remove pages failed, rc = %d\n", __func__, ret); + pr_err("%s: add pages failed, rc = %d\n", __func__, ret); /* Assign back to HLOS if add pages cmd failed */ - if (add_pages) - msm_audio_ion_phys_assign("audio_lib_mem_client", fd, - &paddr, &pa_len, ADSP_TO_HLOS); + 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; @@ -272,9 +301,8 @@ static int msm_transcode_loopback_open(struct snd_compr_stream *cstream) } msm_adsp_init_mixer_ctl_pp_event_queue(rtd); if (pdata->ion_fd[rtd->dai_link->be_id] > 0) { - ret = msm_transcode_map_unmap_fd( - pdata->ion_fd[rtd->dai_link->be_id], - true); + ret = msm_transcode_map_ion_fd(trans, + pdata->ion_fd[rtd->dai_link->be_id]); if (ret < 0) goto exit; } @@ -345,16 +373,13 @@ static int msm_transcode_loopback_free(struct snd_compr_stream *cstream) memset(&trans->sink, 0, sizeof(struct loopback_stream)); msm_adsp_clean_mixer_ctl_pp_event_queue(rtd); if (trans->shm_ion_fd > 0) { - msm_audio_ion_phys_assign("audio_shm_mem_client", - trans->shm_ion_fd, - &paddr, &pa_len, - ADSP_TO_HLOS); + 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_map_unmap_fd( - pdata->ion_fd[rtd->dai_link->be_id], - false); + msm_transcode_unmap_ion_fd(trans); pdata->ion_fd[rtd->dai_link->be_id] = 0; } } else if (cstream->direction == SND_COMPRESS_CAPTURE) { @@ -713,7 +738,9 @@ static int msm_transcode_shm_ion_fd_map_put(struct snd_kcontrol *kcontrol, 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_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 map shm mem\n", __func__); done: diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index 609ae8cd82ac..14f9411104b3 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -7118,7 +7118,8 @@ fail_cmd: return rc; } -int q6asm_audio_map_shm_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) { ion_phys_addr_t paddr; size_t pa_len = 0; @@ -7137,7 +7138,8 @@ int q6asm_audio_map_shm_fd(struct audio_client *ac, int fd) goto fail_cmd; } - ret = msm_audio_ion_phys_assign("audio_shm_mem_client", fd, + ret = msm_audio_ion_phys_assign("audio_shm_mem_client", client, + handle, fd, &paddr, &pa_len, HLOS_TO_ADSP); if (ret) { pr_err("%s: shm ION phys failed, rc = %d\n", __func__, ret); diff --git a/sound/soc/msm/sdm660-internal.c b/sound/soc/msm/sdm660-internal.c index 0f28e100f535..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"); |
