summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/msm/android.txt34
-rw-r--r--Documentation/devicetree/bindings/arm/msm/imem.txt10
-rw-r--r--Documentation/devicetree/bindings/arm/msm/msm.txt21
-rw-r--r--Documentation/devicetree/bindings/display/msm/sde.txt4
-rw-r--r--Documentation/devicetree/bindings/drm/msm/mdp.txt3
-rw-r--r--Documentation/devicetree/bindings/gpu/adreno.txt4
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt2
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-cci.txt3
-rw-r--r--Documentation/devicetree/bindings/mmc/sdhci-msm.txt1
-rw-r--r--Documentation/devicetree/bindings/net/can/k61-can.txt34
-rw-r--r--Documentation/devicetree/bindings/pci/msm_pcie.txt4
-rw-r--r--Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt1
-rw-r--r--Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt2
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt8
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-qnovo.txt1
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt4
-rw-r--r--Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt3
-rw-r--r--Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt7
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/guest_shm.txt19
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/msm-early-cam.txt110
-rw-r--r--Documentation/devicetree/bindings/spi/spidev.txt30
-rw-r--r--Documentation/devicetree/bindings/usb/msm-phy.txt2
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt3
-rw-r--r--Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt18
-rw-r--r--Makefile5
-rw-r--r--android/configs/android-base.cfg6
-rw-r--r--android/configs/android-recommended.cfg5
-rw-r--r--arch/Kconfig4
-rw-r--r--arch/arc/include/asm/cache.h2
-rw-r--r--arch/arc/mm/cache.c13
-rw-r--r--arch/arm/boot/dts/armada-388-gp.dts4
-rw-r--r--arch/arm/boot/dts/omap3-n900.dts2
-rw-r--r--arch/arm/boot/dts/qcom/Makefile36
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts10
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts4
-rw-r--r--arch/arm/boot/dts/qcom/apq8098-v2.1-svr20.dts (renamed from arch/arm/boot/dts/qcom/msm8998-9x55-cdp.dts)14
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi120
-rw-r--r--arch/arm/boot/dts/qcom/fg-gen3-batterydata-svr-v2-3200mah.dtsi84
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm660.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm660l.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmi8998.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm-smb138x.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi106
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi106
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-cv2x.dtsi650
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi60
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-regulator.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-sde.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi109
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts3
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-cv2x.dts26
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-camera-sensor-svr20.dtsi399
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-coresight.dtsi23
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-sde.dtsi31
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-svr20-pinctrl.dtsi3386
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-svr20.dtsi409
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2.dtsi5
-rw-r--r--arch/arm/boot/dts/qcom/msm8998.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/sda630-cdp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sda630-mtp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sda630-pm660a-cdp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sda630-pm660a-mtp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sda636-cdp.dts37
-rw-r--r--arch/arm/boot/dts/qcom/sda636-mtp.dts31
-rw-r--r--arch/arm/boot/dts/qcom/sda636-pm660a-cdp.dts38
-rw-r--r--arch/arm/boot/dts/qcom/sda636-pm660a-mtp.dts32
-rw-r--r--arch/arm/boot/dts/qcom/sda636-pm660a-qrd-hdk.dts242
-rw-r--r--arch/arm/boot/dts/qcom/sda636-pm660a-rcm.dts36
-rw-r--r--arch/arm/boot/dts/qcom/sda636-rcm.dts36
-rw-r--r--arch/arm/boot/dts/qcom/sda636.dtsi (renamed from arch/arm/boot/dts/qcom/msm8998-9x55.dtsi)24
-rw-r--r--arch/arm/boot/dts/qcom/sda658-cdp.dts5
-rw-r--r--arch/arm/boot/dts/qcom/sda658-mtp.dts5
-rw-r--r--arch/arm/boot/dts/qcom/sda658-pm660a-cdp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/sda658-pm660a-mtp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/sda660-cdp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sda660-mtp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sda660-pm660a-cdp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sda660-pm660a-mtp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-camera-sensor-mtp.dtsi20
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-cdp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-headset-jacktype-no-cdp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-internal-codec-cdp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-internal-codec-mtp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-cdp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-mtp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-mtp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-pm.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-pm660a-headset-jacktype-no-cdp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-qrd.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-usbc-audio-mtp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm630.dtsi16
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-camera-sensor-mtp.dtsi50
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-cdp.dts37
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-cdp.dtsi (renamed from arch/arm/boot/dts/qcom/msm8998-9x55-mtp.dts)23
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-headset-jacktype-no-cdp.dts27
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-headset-jacktype-no-rcm.dts26
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-internal-codec-cdp.dts27
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-internal-codec-mtp.dts31
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-cdp.dts28
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-mtp.dts33
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-rcm.dts26
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-internal-codec-rcm.dts26
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-mtp.dts31
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-mtp.dtsi (renamed from arch/arm/boot/dts/qcom/msm8998-9x55-rcm.dts)24
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-pm660a-cdp.dts56
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-pm660a-headset-jacktype-no-cdp.dts40
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-pm660a-headset-jacktype-no-rcm.dts26
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-pm660a-mtp.dts50
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-pm660a-qrd.dts58
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-pm660a-rcm.dts36
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-qrd.dts88
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-qrd.dtsi16
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-rcm.dts36
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-usbc-audio-mtp.dts32
-rw-r--r--arch/arm/boot/dts/qcom/sdm636-usbc-audio-rcm.dts30
-rw-r--r--arch/arm/boot/dts/qcom/sdm636.dtsi73
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-cdp.dts5
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-internal-codec-cdp.dts5
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-internal-codec-mtp.dts5
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-cdp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-mtp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-mtp.dts5
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-pm660a-cdp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-pm660a-mtp.dts6
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-pm660a-qrd.dts6
-rw-r--r--arch/arm/boot/dts/qcom/sdm658-qrd.dts5
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi79
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi79
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-cdp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-gpu.dtsi45
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-headset-jacktype-no-cdp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-internal-codec-cdp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-internal-codec-mtp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-cdp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-mtp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mtp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mtp.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi28
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-pm660a-headset-jacktype-no-cdp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-qrd.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-qrd.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-regulator.dtsi83
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-usbc-audio-mtp.dts3
-rw-r--r--arch/arm/boot/dts/qcom/sdm660.dtsi51
-rw-r--r--arch/arm/boot/dts/qcom/vplatform-lfv-agave.dtsi50
-rw-r--r--arch/arm/boot/dts/qcom/vplatform-lfv-ion.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-pinctrl.dtsi538
-rw-r--r--arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts695
-rw-r--r--arch/arm/boot/dts/qcom/vplatform-lfv-smmu.dtsi75
-rw-r--r--arch/arm/configs/s3c2410_defconfig6
-rw-r--r--arch/arm/configs/sdm660-perf_defconfig6
-rw-r--r--arch/arm/configs/sdm660_defconfig7
-rw-r--r--arch/arm/include/asm/elf.h8
-rw-r--r--arch/arm/include/asm/ftrace.h18
-rw-r--r--arch/arm/kvm/mmu.c4
-rw-r--r--arch/arm64/Kconfig10
-rw-r--r--arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts2
-rw-r--r--arch/arm64/boot/dts/xilinx/zynqmp.dtsi6
-rw-r--r--arch/arm64/configs/msm-auto-gvm-perf_defconfig286
-rw-r--r--arch/arm64/configs/msm-auto-gvm_defconfig316
-rw-r--r--arch/arm64/configs/msm-auto-perf_defconfig6
-rw-r--r--arch/arm64/configs/msm-auto_defconfig6
-rw-r--r--arch/arm64/configs/msm-perf_defconfig2
-rw-r--r--arch/arm64/configs/msm_defconfig2
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig4
-rw-r--r--arch/arm64/configs/msmcortex_defconfig5
-rw-r--r--arch/arm64/configs/sdm660-perf_defconfig17
-rw-r--r--arch/arm64/configs/sdm660_defconfig14
-rw-r--r--arch/arm64/include/asm/assembler.h19
-rw-r--r--arch/arm64/include/asm/current.h35
-rw-r--r--arch/arm64/include/asm/elf.h12
-rw-r--r--arch/arm64/include/asm/io.h4
-rw-r--r--arch/arm64/include/asm/percpu.h2
-rw-r--r--arch/arm64/include/asm/perf_event.h2
-rw-r--r--arch/arm64/include/asm/smp.h16
-rw-r--r--arch/arm64/include/asm/stack_pointer.h9
-rw-r--r--arch/arm64/include/asm/suspend.h2
-rw-r--r--arch/arm64/include/asm/thread_info.h26
-rw-r--r--arch/arm64/kernel/asm-offsets.c14
-rw-r--r--arch/arm64/kernel/entry.S60
-rw-r--r--arch/arm64/kernel/head.S18
-rw-r--r--arch/arm64/kernel/perf_trace_counters.c2
-rw-r--r--arch/arm64/kernel/process.c42
-rw-r--r--arch/arm64/kernel/return_address.c1
-rw-r--r--arch/arm64/kernel/setup.c6
-rw-r--r--arch/arm64/kernel/sleep.S3
-rw-r--r--arch/arm64/kernel/smp.c24
-rw-r--r--arch/arm64/kernel/stacktrace.c7
-rw-r--r--arch/arm64/kernel/suspend.c6
-rw-r--r--arch/arm64/kernel/traps.c22
-rw-r--r--arch/arm64/mm/dma-mapping.c24
-rw-r--r--arch/arm64/mm/fault.c8
-rw-r--r--arch/arm64/mm/proc.S6
-rw-r--r--arch/ia64/Kconfig2
-rw-r--r--arch/ia64/include/asm/thread_info.h8
-rw-r--r--arch/ia64/kernel/init_task.c1
-rw-r--r--arch/mips/include/asm/branch.h5
-rw-r--r--arch/mips/kernel/branch.c38
-rw-r--r--arch/mips/kernel/proc.c2
-rw-r--r--arch/mips/kernel/ptrace.c2
-rw-r--r--arch/mips/kernel/scall32-o32.S2
-rw-r--r--arch/mips/kernel/scall64-64.S2
-rw-r--r--arch/mips/kernel/scall64-n32.S2
-rw-r--r--arch/mips/kernel/scall64-o32.S2
-rw-r--r--arch/mips/kernel/syscall.c15
-rw-r--r--arch/mips/math-emu/cp1emu.c38
-rw-r--r--arch/mn10300/include/asm/thread_info.h2
-rw-r--r--arch/mn10300/kernel/kgdb.c3
-rw-r--r--arch/openrisc/kernel/vmlinux.lds.S2
-rw-r--r--arch/parisc/include/asm/dma-mapping.h11
-rw-r--r--arch/parisc/include/asm/mmu_context.h15
-rw-r--r--arch/parisc/kernel/syscall_table.S2
-rw-r--r--arch/parisc/mm/fault.c2
-rw-r--r--arch/powerpc/include/asm/atomic.h4
-rw-r--r--arch/powerpc/include/asm/elf.h13
-rw-r--r--arch/powerpc/include/asm/reg.h2
-rw-r--r--arch/powerpc/include/asm/topology.h14
-rw-r--r--arch/powerpc/kernel/setup_64.c4
-rw-r--r--arch/powerpc/kvm/book3s_hv.c41
-rw-r--r--arch/powerpc/kvm/book3s_hv_rmhandlers.S41
-rw-r--r--arch/powerpc/lib/sstep.c19
-rw-r--r--arch/powerpc/platforms/pseries/reconfig.c1
-rw-r--r--arch/s390/include/asm/elf.h15
-rw-r--r--arch/s390/include/asm/syscall.h6
-rw-r--r--arch/s390/net/bpf_jit_comp.c3
-rw-r--r--arch/sparc/include/asm/mmu_context_64.h14
-rw-r--r--arch/sparc/include/asm/trap_block.h1
-rw-r--r--arch/sparc/kernel/smp_64.c185
-rw-r--r--arch/sparc/kernel/sun4v_ivec.S15
-rw-r--r--arch/sparc/kernel/traps_64.c1
-rw-r--r--arch/sparc/kernel/tsb.S12
-rw-r--r--arch/sparc/power/hibernate.c3
-rw-r--r--arch/tile/include/asm/thread_info.h2
-rw-r--r--arch/tile/kernel/process.c3
-rw-r--r--arch/x86/boot/string.c1
-rw-r--r--arch/x86/boot/string.h9
-rw-r--r--arch/x86/crypto/sha1_avx2_x86_64_asm.S67
-rw-r--r--arch/x86/entry/entry_64.S2
-rw-r--r--arch/x86/include/asm/elf.h13
-rw-r--r--arch/x86/include/asm/msr-index.h2
-rw-r--r--arch/x86/include/asm/pat.h1
-rw-r--r--arch/x86/include/asm/xen/hypercall.h3
-rw-r--r--arch/x86/kernel/acpi/boot.c8
-rw-r--r--arch/x86/kernel/apic/io_apic.c2
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce_amd.c3
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_lbr.c8
-rw-r--r--arch/x86/kernel/kvm.c6
-rw-r--r--arch/x86/kernel/setup.c7
-rw-r--r--arch/x86/kvm/cpuid.c13
-rw-r--r--arch/x86/kvm/cpuid.h9
-rw-r--r--arch/x86/kvm/vmx.c19
-rw-r--r--arch/x86/lib/copy_user_64.S7
-rw-r--r--arch/x86/mm/pat.c28
-rw-r--r--arch/x86/tools/relocs.c3
-rw-r--r--crypto/authencesn.c5
-rw-r--r--drivers/acpi/apei/ghes.c1
-rw-r--r--drivers/acpi/glue.c10
-rw-r--r--drivers/acpi/ioapic.c6
-rw-r--r--drivers/android/Makefile2
-rw-r--r--drivers/android/binder.c4290
-rw-r--r--drivers/android/binder_alloc.c802
-rw-r--r--drivers/android/binder_alloc.h163
-rw-r--r--drivers/android/binder_trace.h41
-rw-r--r--drivers/ata/libata-scsi.c6
-rw-r--r--drivers/base/core.c14
-rw-r--r--drivers/base/platform.c11
-rw-r--r--drivers/base/power/domain.c14
-rw-r--r--drivers/base/power/main.c12
-rw-r--r--drivers/base/power/sysfs.c2
-rw-r--r--drivers/base/power/wakeup.c30
-rw-r--r--drivers/block/virtio_blk.c3
-rw-r--r--drivers/block/xen-blkback/blkback.c3
-rw-r--r--drivers/block/xen-blkback/xenbus.c8
-rw-r--r--drivers/bluetooth/btfm_slim.c14
-rw-r--r--drivers/bluetooth/btfm_slim_codec.c60
-rw-r--r--drivers/bluetooth/btfm_slim_wcn3990.c42
-rw-r--r--drivers/bluetooth/btfm_slim_wcn3990.h3
-rw-r--r--drivers/char/adsprpc.c99
-rw-r--r--drivers/char/diag/diagchar_core.c21
-rw-r--r--drivers/char/diag/diagfwd_cntl.c13
-rw-r--r--drivers/char/diag/diagfwd_glink.c6
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c86
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c5
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c5
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c7
-rw-r--r--drivers/char/tpm/tpm-chip.c196
-rw-r--r--drivers/char/tpm/tpm-dev.c15
-rw-r--r--drivers/char/tpm/tpm-interface.c50
-rw-r--r--drivers/char/tpm/tpm-sysfs.c20
-rw-r--r--drivers/char/tpm/tpm.h20
-rw-r--r--drivers/char/tpm/tpm2-cmd.c8
-rw-r--r--drivers/char/tpm/tpm_atmel.c14
-rw-r--r--drivers/char/tpm/tpm_i2c_atmel.c16
-rw-r--r--drivers/char/tpm/tpm_i2c_infineon.c6
-rw-r--r--drivers/char/tpm/tpm_i2c_nuvoton.c22
-rw-r--r--drivers/char/tpm/tpm_infineon.c22
-rw-r--r--drivers/char/tpm/tpm_nsc.c20
-rw-r--r--drivers/char/tpm/tpm_tis.c16
-rw-r--r--drivers/clk/msm/clock-dummy.c7
-rw-r--r--drivers/clk/msm/clock-osm.c28
-rw-r--r--drivers/clk/qcom/clk-cpu-osm.c57
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c9
-rw-r--r--drivers/crypto/atmel-sha.c4
-rw-r--r--drivers/crypto/caam/caamhash.c2
-rw-r--r--drivers/crypto/caam/key_gen.c2
-rw-r--r--drivers/crypto/msm/ice.c5
-rw-r--r--drivers/crypto/msm/qce50.c20
-rw-r--r--drivers/crypto/msm/qce50.h4
-rw-r--r--drivers/crypto/msm/qcrypto.c14
-rw-r--r--drivers/crypto/talitos.c7
-rw-r--r--drivers/dma/ioat/hw.h2
-rw-r--r--drivers/dma/ioat/init.c11
-rw-r--r--drivers/dma/ti-dma-crossbar.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c3
-rw-r--r--drivers/gpu/drm/drm_atomic.c5
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c41
-rw-r--r--drivers/gpu/drm/drm_edid.c54
-rw-r--r--drivers/gpu/drm/drm_gem.c6
-rw-r--r--drivers/gpu/drm/msm/Kconfig10
-rw-r--r--drivers/gpu/drm/msm/Makefile2
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.c18
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.h8
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_preempt.c17
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_device.c3
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c226
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h45
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c56
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h8
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c117
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h13
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h1
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c91
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c33
-rw-r--r--drivers/gpu/drm/msm/msm_mmu.h2
-rw-r--r--drivers/gpu/drm/msm/msm_smmu.c24
-rw-r--r--drivers/gpu/drm/msm/sde/sde_color_processing.c4
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.c219
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.h63
-rw-r--r--drivers/gpu/drm/msm/sde/sde_core_irq.c2
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.c319
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.h10
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder.c121
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder_phys.h2
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c74
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.c168
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.h12
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_cdm.c4
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_ctl.c66
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_ctl.h20
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_dspp.c7
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_intf.c10
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_lm.c10
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_mdss.h5
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_pingpong.c7
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_sspp.c37
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_top.c11
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_util.h2
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_vbif.c6
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_wb.c7
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c177
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.h52
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms_utils.c26
-rw-r--r--drivers/gpu/drm/msm/sde/sde_plane.c41
-rw-r--r--drivers/gpu/drm/msm/sde/sde_splash.c682
-rw-r--r--drivers/gpu/drm/msm/sde/sde_splash.h132
-rw-r--r--drivers/gpu/drm/msm/sde_dbg.c2067
-rw-r--r--drivers/gpu/drm/msm/sde_dbg.h292
-rw-r--r--drivers/gpu/drm/msm/sde_dbg_evtlog.c257
-rw-r--r--drivers/gpu/drm/msm/sde_edid_parser.c6
-rw-r--r--drivers/gpu/drm/msm/sde_hdcp_1x.c30
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c2
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c13
-rw-r--r--drivers/gpu/drm/radeon/ci_dpm.c6
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c26
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.h2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.c185
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c11
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c17
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c11
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c8
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_vgacon.c11
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_lvds_regs.h2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_fb.c2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_object.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c2
-rw-r--r--drivers/gpu/msm/adreno.c38
-rw-r--r--drivers/gpu/msm/adreno.h47
-rw-r--r--drivers/gpu/msm/adreno_a3xx.c6
-rw-r--r--drivers/gpu/msm/adreno_a4xx.c6
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c11
-rw-r--r--drivers/gpu/msm/adreno_a5xx_preempt.c43
-rw-r--r--drivers/gpu/msm/adreno_debugfs.c6
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c4
-rw-r--r--drivers/gpu/msm/adreno_ringbuffer.c60
-rw-r--r--drivers/gpu/msm/adreno_trace.h25
-rw-r--r--drivers/gpu/msm/kgsl.c73
-rw-r--r--drivers/gpu/msm/kgsl_drawobj.c74
-rw-r--r--drivers/gpu/msm/kgsl_drawobj.h4
-rw-r--r--drivers/gpu/msm/kgsl_log.h11
-rw-r--r--drivers/gpu/msm/kgsl_pool.c40
-rw-r--r--drivers/gpu/msm/kgsl_pool.h3
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c17
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.h20
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.c1
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.h13
-rw-r--r--drivers/gpu/msm/kgsl_sync.c4
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-ids.h3
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c14
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c9
-rw-r--r--drivers/iio/adc/qcom-rradc.c78
-rw-r--r--drivers/iio/adc/vf610_adc.c2
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c8
-rw-r--r--drivers/iio/imu/adis16480.c2
-rw-r--r--drivers/iio/light/tsl2563.c2
-rw-r--r--drivers/infiniband/core/cma.c2
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c9
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c2
-rw-r--r--drivers/input/misc/hbtp_input.c160
-rw-r--r--drivers/input/misc/keychord.c90
-rw-r--r--drivers/input/mouse/elan_i2c_core.c5
-rw-r--r--drivers/input/mouse/trackpoint.c3
-rw-r--r--drivers/input/mouse/trackpoint.h3
-rw-r--r--drivers/input/serio/i8042.c12
-rw-r--r--drivers/input/touchscreen/st/fts.c19
-rw-r--r--drivers/iommu/arm-smmu.c3
-rw-r--r--drivers/iommu/dma-iommu.c10
-rw-r--r--drivers/irqchip/irq-atmel-aic-common.c5
-rw-r--r--drivers/irqchip/irq-gic-v3.c3
-rw-r--r--drivers/irqchip/irq-keystone.c28
-rw-r--r--drivers/irqchip/irq-mxs.c4
-rw-r--r--drivers/isdn/i4l/isdn_common.c1
-rw-r--r--drivers/isdn/i4l/isdn_net.c5
-rw-r--r--drivers/isdn/i4l/isdn_ppp.c2
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c43
-rw-r--r--drivers/leds/leds-qpnp-wled.c414
-rw-r--r--drivers/leds/leds-qpnp.c29
-rw-r--r--drivers/mailbox/mailbox.c13
-rw-r--r--drivers/md/md.c4
-rw-r--r--drivers/md/raid1.c5
-rw-r--r--drivers/md/raid5.c11
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c2
-rw-r--r--drivers/media/pci/cx88/cx88-cards.c9
-rw-r--r--drivers/media/pci/cx88/cx88-video.c2
-rw-r--r--drivers/media/pci/saa7134/saa7134-i2c.c31
-rw-r--r--drivers/media/pci/saa7164/saa7164-bus.c13
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c22
-rw-r--r--drivers/media/platform/msm/ais/fd/msm_fd_dev.c27
-rw-r--r--drivers/media/platform/msm/ais/fd/msm_fd_dev.h1
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp.h1
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp47.c10
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c6
-rw-r--r--drivers/media/platform/msm/ais/msm.c3
-rw-r--r--drivers/media/platform/msm/ais/sensor/cci/Makefile1
-rw-r--r--drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.c279
-rw-r--r--drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.h53
-rw-r--r--drivers/media/platform/msm/ais/sensor/msm_sensor.c125
-rw-r--r--drivers/media/platform/msm/ais/sensor/msm_sensor.h10
-rw-r--r--drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c130
-rw-r--r--drivers/media/platform/msm/ais/sensor/msm_sensor_init.c33
-rw-r--r--drivers/media/platform/msm/ais/sensor/ois/msm_ois.c8
-rw-r--r--drivers/media/platform/msm/camera_v2/camera/camera.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c18
-rw-r--r--drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c21
-rw-r--r--drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.c30
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c3
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.c34
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.h1
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c87
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c25
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c23
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c6
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c8
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.c6
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc.c29
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.c2
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c8
-rw-r--r--drivers/media/rc/imon.c2
-rw-r--r--drivers/media/rc/ir-lirc-codec.c2
-rw-r--r--drivers/misc/Kconfig8
-rw-r--r--drivers/misc/enclosure.c14
-rw-r--r--drivers/misc/mei/hw-me-regs.h5
-rw-r--r--drivers/misc/mei/pci-me.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c2
-rw-r--r--drivers/misc/qseecom.c5
-rw-r--r--drivers/misc/uid_sys_stats.c304
-rw-r--r--drivers/mmc/card/block.c9
-rw-r--r--drivers/mmc/core/bus.c1
-rw-r--r--drivers/mmc/core/sd.c1
-rw-r--r--drivers/mmc/host/sdhci-msm.c10
-rw-r--r--drivers/mmc/host/sdhci.c19
-rw-r--r--drivers/net/can/spi/Kconfig6
-rw-r--r--drivers/net/can/spi/Makefile1
-rw-r--r--drivers/net/can/spi/k61.c936
-rw-r--r--drivers/net/can/spi/rh850.c2
-rw-r--r--drivers/net/ethernet/aurora/nb8800.c9
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c5
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c19
-rw-r--r--drivers/net/ethernet/msm/msm_rmnet_mhi.c9
-rw-r--r--drivers/net/ethernet/realtek/r8169.c1
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c2
-rw-r--r--drivers/net/irda/mcs7780.c16
-rw-r--r--drivers/net/phy/dp83640.c2
-rw-r--r--drivers/net/phy/dp83867.c10
-rw-r--r--drivers/net/phy/micrel.c2
-rw-r--r--drivers/net/phy/phy.c12
-rw-r--r--drivers/net/phy/phy_device.c4
-rw-r--r--drivers/net/usb/kaweth.c10
-rw-r--r--drivers/net/usb/qmi_wwan.c1
-rw-r--r--drivers/net/vrf.c18
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c8
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c18
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h1
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c173
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.h90
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c1
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c21
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.h33
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c19
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c16
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-ops.h6
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/tx99.c13
-rw-r--r--drivers/net/wireless/ath/wil6210/Kconfig12
-rw-r--r--drivers/net/wireless/ath/wil6210/Makefile2
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c83
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c24
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c14
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c42
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c3
-rw-r--r--drivers/net/wireless/ath/wil6210/pm.c27
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c6
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h19
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c14
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.h720
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c5
-rw-r--r--drivers/net/wireless/cnss/Kconfig1
-rw-r--r--drivers/net/wireless/cnss/cnss_pci.c370
-rw-r--r--drivers/net/wireless/cnss/cnss_sdio.c7
-rw-r--r--drivers/net/wireless/cnss2/debug.c330
-rw-r--r--drivers/net/wireless/cnss2/main.c29
-rw-r--r--drivers/net/wireless/cnss2/main.h6
-rw-r--r--drivers/net/wireless/cnss2/pci.c37
-rw-r--r--drivers/net/wireless/cnss2/qmi.c2
-rw-r--r--drivers/net/wireless/cnss2/wlan_firmware_service_v01.h6
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c2
-rw-r--r--drivers/net/wireless/ti/wlcore/spi.c2
-rw-r--r--drivers/net/wireless/wcnss/wcnss_vreg.c189
-rw-r--r--drivers/net/wireless/wcnss/wcnss_wlan.c331
-rw-r--r--drivers/net/xen-netback/common.h1
-rw-r--r--drivers/net/xen-netback/interface.c6
-rw-r--r--drivers/net/xen-netback/netback.c6
-rw-r--r--drivers/nfc/fdp/i2c.c16
-rw-r--r--drivers/nfc/nfcmrvl/fw_dnld.c7
-rw-r--r--drivers/nfc/nfcmrvl/main.c35
-rw-r--r--drivers/nfc/nfcmrvl/uart.c8
-rw-r--r--drivers/nfc/st21nfca/dep.c3
-rw-r--r--drivers/nfc/st21nfca/se.c18
-rw-r--r--drivers/ntb/ntb_transport.c8
-rw-r--r--drivers/nvdimm/btt.c9
-rw-r--r--drivers/nvmem/imx-ocotp.c2
-rw-r--r--drivers/of/device.c2
-rw-r--r--drivers/parisc/ccio-dma.c12
-rw-r--r--drivers/parisc/dino.c7
-rw-r--r--drivers/parisc/lba_pci.c6
-rw-r--r--drivers/parisc/sba_iommu.c14
-rw-r--r--drivers/pci/host/pci-msm.c43
-rw-r--r--drivers/pci/pci-driver.c1
-rw-r--r--drivers/perf/arm_pmu.c1
-rw-r--r--drivers/pinctrl/freescale/pinctrl-mxs.c16
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson8b.c4
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos.c4
-rw-r--r--drivers/pinctrl/sh-pfc/core.c3
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7791.c2
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c2
-rw-r--r--drivers/platform/msm/gsi/gsi.c14
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_usb.c18
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c28
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_i.h10
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c62
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_rt.c15
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c9
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c325
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h15
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_nat.c59
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c5
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h25
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c466
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_rt.c20
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c16
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c22
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h2
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c14
-rw-r--r--drivers/platform/msm/mhi/mhi_iface.c2
-rw-r--r--drivers/platform/msm/mhi/mhi_init.c4
-rw-r--r--drivers/platform/msm/mhi/mhi_mmio_ops.c5
-rw-r--r--drivers/platform/msm/mhi/mhi_sys.c4
-rw-r--r--drivers/platform/msm/mhi_uci/mhi_uci.c116
-rw-r--r--drivers/platform/msm/qpnp-revid.c11
-rw-r--r--drivers/platform/msm/sps/sps.c3
-rw-r--r--drivers/platform/msm/usb_bam.c169
-rw-r--r--drivers/power/power_supply_sysfs.c8
-rw-r--r--drivers/power/supply/qcom/fg-core.h40
-rw-r--r--drivers/power/supply/qcom/fg-util.c34
-rw-r--r--drivers/power/supply/qcom/pmic-voter.c17
-rw-r--r--drivers/power/supply/qcom/qpnp-fg-gen3.c1173
-rw-r--r--drivers/power/supply/qcom/qpnp-qnovo.c415
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c107
-rw-r--r--drivers/power/supply/qcom/smb-lib.c451
-rw-r--r--drivers/power/supply/qcom/smb-lib.h25
-rw-r--r--drivers/power/supply/qcom/smb138x-charger.c76
-rw-r--r--drivers/power/supply/qcom/step-chg-jeita.c333
-rw-r--r--drivers/power/supply/qcom/step-chg-jeita.h2
-rw-r--r--drivers/pwm/pwm-qpnp.c67
-rw-r--r--drivers/regulator/cprh-kbss-regulator.c13
-rw-r--r--drivers/regulator/qpnp-lcdb-regulator.c84
-rw-r--r--drivers/scsi/fnic/fnic.h1
-rw-r--r--drivers/scsi/fnic/fnic_scsi.c16
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.c33
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c18
-rw-r--r--drivers/scsi/snic/snic_main.c3
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c7
-rw-r--r--drivers/soc/qcom/Kconfig2
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/common_log.c27
-rw-r--r--drivers/soc/qcom/glink.c35
-rw-r--r--drivers/soc/qcom/glink_smem_native_xprt.c1
-rw-r--r--drivers/soc/qcom/glink_spi_xprt.c78
-rw-r--r--drivers/soc/qcom/hab/Kconfig7
-rw-r--r--drivers/soc/qcom/hab/Makefile14
-rw-r--r--drivers/soc/qcom/hab/hab.c726
-rw-r--r--drivers/soc/qcom/hab/hab.h415
-rw-r--r--drivers/soc/qcom/hab/hab_grantable.h29
-rw-r--r--drivers/soc/qcom/hab/hab_mem_linux.c451
-rw-r--r--drivers/soc/qcom/hab/hab_mimex.c394
-rw-r--r--drivers/soc/qcom/hab/hab_msg.c208
-rw-r--r--drivers/soc/qcom/hab/hab_open.c154
-rw-r--r--drivers/soc/qcom/hab/hab_pchan.c86
-rw-r--r--drivers/soc/qcom/hab/hab_pipe.c131
-rw-r--r--drivers/soc/qcom/hab/hab_pipe.h60
-rw-r--r--drivers/soc/qcom/hab/hab_qvm.c251
-rw-r--r--drivers/soc/qcom/hab/hab_qvm.h47
-rw-r--r--drivers/soc/qcom/hab/hab_vchan.c184
-rw-r--r--drivers/soc/qcom/hab/khab.c140
-rw-r--r--drivers/soc/qcom/hab/qvm_comm.c95
-rw-r--r--drivers/soc/qcom/icnss.c75
-rw-r--r--drivers/soc/qcom/ipc_router_glink_xprt.c5
-rw-r--r--drivers/soc/qcom/jtagv8-etm.c13
-rw-r--r--drivers/soc/qcom/jtagv8.c7
-rw-r--r--drivers/soc/qcom/peripheral-loader.c108
-rw-r--r--drivers/soc/qcom/peripheral-loader.h28
-rw-r--r--drivers/soc/qcom/pil-msa.c10
-rw-r--r--drivers/soc/qcom/qbt1000.c101
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr.c1
-rw-r--r--drivers/soc/qcom/qdsp6v2/msm_audio_ion.c120
-rw-r--r--drivers/soc/qcom/qpnp-haptic.c14
-rw-r--r--drivers/soc/qcom/ramdump.c133
-rw-r--r--drivers/soc/qcom/scm.c95
-rw-r--r--drivers/soc/qcom/scm_qcpe.c58
-rw-r--r--drivers/soc/qcom/secure_buffer.c5
-rw-r--r--drivers/soc/qcom/smp2p_spinlock_test.c4
-rw-r--r--drivers/soc/qcom/socinfo.c12
-rw-r--r--drivers/soc/qcom/spcom.c42
-rw-r--r--drivers/soc/qcom/wcd-dsp-glink.c38
-rw-r--r--drivers/spi/spi-dw.c5
-rw-r--r--drivers/spi/spi_qsd.c12
-rw-r--r--drivers/spi/spidev.c2
-rw-r--r--drivers/spmi/spmi.c12
-rw-r--r--drivers/staging/android/ion/ion.c16
-rw-r--r--drivers/staging/android/ion/ion_cma_heap.c24
-rw-r--r--drivers/staging/android/ion/ion_priv.h6
-rw-r--r--drivers/staging/android/lowmemorykiller.c4
-rw-r--r--drivers/staging/comedi/comedi_fops.c7
-rw-r--r--drivers/staging/iio/resolver/ad2s1210.c2
-rw-r--r--drivers/staging/rtl8188eu/os_dep/usb_intf.c2
-rw-r--r--drivers/staging/vt6656/main_usb.c3
-rw-r--r--drivers/target/iscsi/iscsi_target.c40
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.c2
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.c6
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.h2
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c11
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c214
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.c41
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.h2
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c19
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.h1
-rw-r--r--drivers/target/target_core_fabric_configfs.c5
-rw-r--r--drivers/target/target_core_tpg.c4
-rw-r--r--drivers/target/target_core_transport.c9
-rw-r--r--drivers/thermal/cpu_cooling.c4
-rw-r--r--drivers/thermal/msm_thermal.c8
-rw-r--r--drivers/tty/serial/msm_serial_hs.c5
-rw-r--r--drivers/tty/vt/vt.c6
-rw-r--r--drivers/usb/class/cdc-acm.c3
-rw-r--r--drivers/usb/core/hcd.c4
-rw-r--r--drivers/usb/core/hub.c10
-rw-r--r--drivers/usb/core/quirks.c8
-rw-r--r--drivers/usb/core/usb-acpi.c26
-rw-r--r--drivers/usb/dwc3/core.c2
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c70
-rw-r--r--drivers/usb/dwc3/dwc3-st.c2
-rw-r--r--drivers/usb/dwc3/ep0.c2
-rw-r--r--drivers/usb/gadget/configfs.c12
-rw-r--r--drivers/usb/gadget/function/f_accessory.c14
-rw-r--r--drivers/usb/gadget/function/f_ccid.c15
-rw-r--r--drivers/usb/gadget/function/f_gsi.c55
-rw-r--r--drivers/usb/gadget/function/f_gsi.h13
-rw-r--r--drivers/usb/gadget/function/f_hid.c2
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c6
-rw-r--r--drivers/usb/gadget/function/f_rndis.c6
-rw-r--r--drivers/usb/host/pci-quirks.c17
-rw-r--r--drivers/usb/host/xhci-hub.c3
-rw-r--r--drivers/usb/host/xhci-ring.c11
-rw-r--r--drivers/usb/musb/musb_host.c1
-rw-r--r--drivers/usb/pd/policy_engine.c52
-rw-r--r--drivers/usb/pd/qpnp-pdphy.c34
-rw-r--r--drivers/usb/pd/usbpd.h14
-rw-r--r--drivers/usb/phy/phy-msm-qusb.c38
-rw-r--r--drivers/usb/phy/phy-msm-ssusb-qmp.c10
-rw-r--r--drivers/usb/renesas_usbhs/common.c4
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c31
-rw-r--r--drivers/usb/serial/cp210x.c2
-rw-r--r--drivers/usb/serial/option.c6
-rw-r--r--drivers/usb/serial/pl2303.c1
-rw-r--r--drivers/usb/serial/pl2303.h1
-rw-r--r--drivers/usb/serial/qcserial.c1
-rw-r--r--drivers/usb/storage/isd200.c5
-rw-r--r--drivers/usb/storage/unusual_uas.h4
-rw-r--r--drivers/usb/usbip/stub_main.c4
-rw-r--r--drivers/usb/usbip/stub_tx.c4
-rw-r--r--drivers/vfio/pci/vfio_pci.c4
-rw-r--r--drivers/vfio/pci/vfio_pci_rdwr.c5
-rw-r--r--drivers/vfio/vfio.c46
-rw-r--r--drivers/video/fbdev/cobalt_lcdfb.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c15
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c19
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c20
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c39
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c7
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_debug.c15
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c9
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c40
-rw-r--r--drivers/video/fbdev/msm/mdss_rotator.c12
-rw-r--r--drivers/xen/biomerge.c3
-rw-r--r--fs/binfmt_elf.c59
-rw-r--r--fs/btrfs/inode.c11
-rw-r--r--fs/ceph/dir.c5
-rw-r--r--fs/cifs/dir.c18
-rw-r--r--fs/cifs/smb2pdu.c4
-rw-r--r--fs/dcache.c27
-rw-r--r--fs/debugfs/inode.c10
-rw-r--r--fs/exec.c11
-rw-r--r--fs/ext4/file.c3
-rw-r--r--fs/ext4/resize.c3
-rw-r--r--fs/ext4/sysfs.c2
-rw-r--r--fs/f2fs/acl.c2
-rw-r--r--fs/fcntl.c14
-rw-r--r--fs/fuse/file.c2
-rw-r--r--fs/gfs2/glock.c11
-rw-r--r--fs/gfs2/incore.h1
-rw-r--r--fs/mount.h1
-rw-r--r--fs/namei.c8
-rw-r--r--fs/namespace.c1
-rw-r--r--fs/nfs/Kconfig1
-rw-r--r--fs/nfs/dir.c12
-rw-r--r--fs/nfs/flexfilelayout/flexfilelayoutdev.c1
-rw-r--r--fs/nfs/inode.c4
-rw-r--r--fs/nfsd/nfs4xdr.c6
-rw-r--r--fs/notify/fsnotify.c8
-rw-r--r--fs/open.c6
-rw-r--r--fs/pnode.c212
-rw-r--r--fs/pstore/ram.c5
-rw-r--r--fs/pstore/ram_core.c25
-rw-r--r--fs/sdcardfs/file.c16
-rw-r--r--fs/sdcardfs/inode.c6
-rw-r--r--fs/sdcardfs/main.c47
-rw-r--r--fs/seq_file.c7
-rw-r--r--fs/udf/inode.c4
-rw-r--r--fs/xfs/xfs_aops.c21
-rw-r--r--include/drm/drm_crtc.h6
-rw-r--r--include/drm/drm_edid.h9
-rw-r--r--include/linux/cpuset.h7
-rw-r--r--include/linux/dcache.h6
-rw-r--r--include/linux/device.h2
-rw-r--r--include/linux/dma-iommu.h1
-rw-r--r--include/linux/fcntl.h6
-rw-r--r--include/linux/fsnotify.h31
-rw-r--r--include/linux/habmm.h38
-rw-r--r--include/linux/init_task.h11
-rw-r--r--include/linux/ipa.h25
-rw-r--r--include/linux/kdb.h2
-rw-r--r--include/linux/mm_types.h4
-rw-r--r--include/linux/msm_audio_ion.h18
-rw-r--r--include/linux/phy.h4
-rw-r--r--include/linux/power_supply.h8
-rw-r--r--include/linux/pstore_ram.h11
-rw-r--r--include/linux/qpnp/qpnp-revid.h1
-rw-r--r--include/linux/restart_block.h51
-rw-r--r--include/linux/sched.h102
-rw-r--r--include/linux/seq_file.h13
-rw-r--r--include/linux/serial_core.h2
-rw-r--r--include/linux/slab.h4
-rw-r--r--include/linux/thread_info.h43
-rw-r--r--include/linux/tick.h1
-rw-r--r--include/linux/usb/hcd.h4
-rw-r--r--include/linux/usb_bam.h6
-rw-r--r--include/linux/vfio.h2
-rw-r--r--include/linux/wcnss_wlan.h8
-rw-r--r--include/linux/workqueue.h4
-rw-r--r--include/net/ip.h4
-rw-r--r--include/net/ip6_route.h8
-rw-r--r--include/net/iw_handler.h3
-rw-r--r--include/net/sch_generic.h5
-rw-r--r--include/net/sctp/sctp.h4
-rw-r--r--include/soc/qcom/minidump.h5
-rw-r--r--include/soc/qcom/ramdump.h5
-rw-r--r--include/soc/qcom/scm.h8
-rw-r--r--include/soc/qcom/socinfo.h7
-rw-r--r--include/sound/apr_audio-v2.h2
-rw-r--r--include/sound/q6asm-v2.h5
-rw-r--r--include/sound/q6core.h16
-rw-r--r--include/target/iscsi/iscsi_target_core.h11
-rw-r--r--include/target/target_core_base.h1
-rw-r--r--include/trace/events/sched.h42
-rw-r--r--include/uapi/drm/msm_drm.h6
-rw-r--r--include/uapi/drm/sde_drm.h10
-rw-r--r--include/uapi/linux/Kbuild1
-rw-r--r--include/uapi/linux/android/binder.h63
-rw-r--r--include/uapi/linux/habmm.h143
-rw-r--r--include/uapi/linux/ipa_qmi_service_v01.h303
-rw-r--r--include/uapi/linux/msm_ipa.h56
-rw-r--r--include/uapi/media/ais/msm_ais_sensor.h21
-rw-r--r--include/uapi/media/ais/msm_ais_sensor_sdk.h8
-rw-r--r--include/uapi/media/msm_camsensor_sdk.h2
-rw-r--r--include/uapi/sound/compress_offload.h7
-rw-r--r--init/Kconfig10
-rw-r--r--init/init_task.c7
-rw-r--r--init/main.c4
-rw-r--r--ipc/mqueue.c4
-rw-r--r--kernel/audit_watch.c12
-rw-r--r--kernel/bpf/verifier.c5
-rw-r--r--kernel/cpuset.c1
-rw-r--r--kernel/events/core.c112
-rw-r--r--kernel/events/hw_breakpoint.c2
-rw-r--r--kernel/extable.c2
-rw-r--r--kernel/fork.c57
-rw-r--r--kernel/kthread.c8
-rw-r--r--kernel/locking/mutex-debug.c12
-rw-r--r--kernel/locking/mutex-debug.h4
-rw-r--r--kernel/locking/mutex.c6
-rw-r--r--kernel/locking/mutex.h2
-rw-r--r--kernel/locking/rwsem-xadd.c35
-rw-r--r--kernel/panic.c2
-rw-r--r--kernel/printk/printk.c5
-rw-r--r--kernel/resource.c13
-rw-r--r--kernel/sched/core.c36
-rw-r--r--kernel/sched/cpufreq_schedutil.c154
-rw-r--r--kernel/sched/cpupri.c11
-rw-r--r--kernel/sched/fair.c408
-rw-r--r--kernel/sched/rt.c15
-rw-r--r--kernel/sched/sched.h4
-rw-r--r--kernel/sched/walt.c8
-rw-r--r--kernel/sched/walt.h2
-rw-r--r--kernel/signal.c4
-rw-r--r--kernel/softirq.c5
-rw-r--r--kernel/sysctl.c3
-rw-r--r--kernel/time/alarmtimer.c3
-rw-r--r--kernel/time/tick-sched.c12
-rw-r--r--kernel/trace/ftrace.c2
-rw-r--r--kernel/trace/trace.c101
-rw-r--r--kernel/trace/trace_events_filter.c4
-rw-r--r--kernel/trace/trace_kprobe.c21
-rw-r--r--kernel/workqueue.c23
-rw-r--r--lib/Kconfig.debug2
-rw-r--r--lib/stackdepot.c1
-rw-r--r--mm/internal.h5
-rw-r--r--mm/memory.c1
-rw-r--r--mm/mempolicy.c5
-rw-r--r--mm/mempool.c2
-rw-r--r--mm/migrate.c11
-rw-r--r--mm/mmap.c2
-rw-r--r--mm/mprotect.c1
-rw-r--r--mm/mremap.c1
-rw-r--r--mm/page_alloc.c12
-rw-r--r--mm/rmap.c36
-rw-r--r--mm/vmscan.c2
-rw-r--r--net/8021q/vlan.c5
-rw-r--r--net/8021q/vlan.h2
-rw-r--r--net/8021q/vlan_dev.c20
-rw-r--r--net/bluetooth/bnep/core.c11
-rw-r--r--net/bluetooth/cmtp/core.c17
-rw-r--r--net/bluetooth/hidp/core.c33
-rw-r--r--net/bluetooth/smp.c35
-rw-r--r--net/core/dev.c45
-rw-r--r--net/core/dev_ioctl.c1
-rw-r--r--net/core/rtnetlink.c3
-rw-r--r--net/dccp/feat.c7
-rw-r--r--net/dccp/ipv4.c1
-rw-r--r--net/dccp/ipv6.c1
-rw-r--r--net/dccp/proto.c19
-rw-r--r--net/ipv4/fib_frontend.c9
-rw-r--r--net/ipv4/fib_semantics.c12
-rw-r--r--net/ipv4/ip_output.c7
-rw-r--r--net/ipv4/route.c2
-rw-r--r--net/ipv4/syncookies.c1
-rw-r--r--net/ipv4/tcp.c2
-rw-r--r--net/ipv4/tcp_input.c17
-rw-r--r--net/ipv4/tcp_output.c3
-rw-r--r--net/ipv4/tcp_timer.c3
-rw-r--r--net/ipv4/udp.c2
-rw-r--r--net/ipv4/udp_offload.c2
-rw-r--r--net/ipv6/addrconf.c27
-rw-r--r--net/ipv6/ip6_fib.c31
-rw-r--r--net/ipv6/ip6_output.c11
-rw-r--r--net/ipv6/output_core.c8
-rw-r--r--net/ipv6/route.c8
-rw-r--r--net/ipv6/syncookies.c1
-rw-r--r--net/ipv6/udp_offload.c2
-rw-r--r--net/irda/af_irda.c2
-rw-r--r--net/key/af_key.c141
-rw-r--r--net/netfilter/ipvs/ip_vs_core.c19
-rw-r--r--net/netfilter/nf_conntrack_extend.c13
-rw-r--r--net/netfilter/xt_qtaguid.c51
-rw-r--r--net/netfilter/xt_qtaguid_internal.h4
-rw-r--r--net/nfc/core.c31
-rw-r--r--net/nfc/hci/core.c10
-rw-r--r--net/nfc/llcp_sock.c9
-rw-r--r--net/nfc/nci/core.c3
-rw-r--r--net/nfc/netlink.c4
-rw-r--r--net/openvswitch/conntrack.c7
-rw-r--r--net/packet/af_packet.c15
-rw-r--r--net/rds/tcp_listen.c2
-rw-r--r--net/rmnet_data/rmnet_data_handlers.c10
-rw-r--r--net/sched/act_ipt.c4
-rw-r--r--net/sched/sch_api.c3
-rw-r--r--net/sched/sch_hhf.c8
-rw-r--r--net/sched/sch_mq.c10
-rw-r--r--net/sched/sch_mqprio.c19
-rw-r--r--net/sched/sch_sfq.c8
-rw-r--r--net/sctp/ipv6.c2
-rw-r--r--net/tipc/netlink_compat.c6
-rw-r--r--net/wireless/db.txt14
-rw-r--r--net/wireless/nl80211.c8
-rw-r--r--net/xfrm/xfrm_policy.c9
-rw-r--r--net/xfrm/xfrm_user.c18
-rw-r--r--scripts/Makefile.lib2
-rwxr-xr-xscripts/checkpatch.pl6
-rw-r--r--security/keys/encrypted-keys/encrypted.c2
-rw-r--r--sound/core/control.c2
-rw-r--r--sound/core/pcm_native.c4
-rw-r--r--sound/core/seq/seq_clientmgr.c13
-rw-r--r--sound/core/seq/seq_queue.c14
-rw-r--r--sound/core/seq/seq_queue.h2
-rw-r--r--sound/pci/hda/patch_conexant.c1
-rw-r--r--sound/pci/hda/patch_realtek.c1
-rw-r--r--sound/soc/codecs/nau8825.c3
-rw-r--r--sound/soc/codecs/nau8825.h3
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c27
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h1
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c95
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h2
-rw-r--r--sound/soc/codecs/tlv320aic3x.c13
-rw-r--r--sound/soc/codecs/wcd-dsp-mgr.c23
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c9
-rw-r--r--sound/soc/codecs/wsa881x.c2
-rw-r--r--sound/soc/generic/simple-card.c2
-rw-r--r--sound/soc/msm/Kconfig4
-rw-r--r--sound/soc/msm/apq8096-auto.c7
-rw-r--r--sound/soc/msm/msm-dai-fe.c54
-rw-r--r--sound/soc/msm/msm-pcm-hostless.c6
-rw-r--r--sound/soc/msm/msm8996.c9
-rw-r--r--sound/soc/msm/msm8998.c36
-rw-r--r--sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c60
-rw-r--r--sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c382
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c3
-rw-r--r--sound/soc/msm/qdsp6v2/msm-lsm-client.c14
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c12
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c377
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h10
-rw-r--r--sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c482
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c16
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c38
-rw-r--r--sound/soc/msm/qdsp6v2/q6core.c62
-rw-r--r--sound/soc/msm/sdm660-ext-dai-links.c6
-rw-r--r--sound/soc/msm/sdm660-internal.c9
-rw-r--r--sound/soc/sh/rcar/adg.c2
-rw-r--r--sound/soc/sh/rcar/core.c21
-rw-r--r--sound/soc/sh/rcar/src.c18
-rw-r--r--sound/soc/sh/rcar/ssi.c3
-rw-r--r--sound/soc/soc-core.c3
-rw-r--r--sound/soc/soc-pcm.c7
-rw-r--r--sound/usb/endpoint.c3
-rw-r--r--sound/usb/mixer.c2
-rw-r--r--sound/usb/mixer.h1
-rw-r--r--sound/usb/mixer_quirks.c6
-rw-r--r--sound/usb/quirks.c1
-rw-r--r--tools/include/linux/compiler.h9
-rw-r--r--tools/lib/lockdep/uinclude/linux/lockdep.h2
-rw-r--r--tools/lib/traceevent/plugin_sched_switch.c4
-rw-r--r--tools/perf/Makefile.perf4
-rw-r--r--tools/perf/arch/x86/tests/intel-cqm.c2
-rw-r--r--tools/perf/arch/x86/util/dwarf-regs.c8
-rw-r--r--tools/perf/bench/numa.c6
-rw-r--r--tools/perf/builtin-script.c70
-rw-r--r--tools/perf/builtin-top.c2
-rw-r--r--tools/perf/builtin-trace.c1
-rw-r--r--tools/perf/tests/parse-events.c8
-rw-r--r--tools/perf/ui/browser.c2
-rw-r--r--tools/perf/ui/browsers/annotate.c4
-rw-r--r--tools/perf/util/event.c12
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-decoder.c121
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c26
-rw-r--r--tools/perf/util/parse-events.c60
-rw-r--r--tools/perf/util/pmu.c2
-rw-r--r--tools/perf/util/scripting-engines/Build2
-rw-r--r--tools/perf/util/strfilter.c1
-rw-r--r--tools/perf/util/string.c2
-rw-r--r--tools/perf/util/symbol-elf.c6
-rw-r--r--tools/perf/util/thread.c2
-rw-r--r--tools/perf/util/thread_map.c10
-rw-r--r--tools/testing/selftests/capabilities/test_execve.c7
-rw-r--r--virt/kvm/vfio.c27
1055 files changed, 39045 insertions, 7615 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 41cbb91351bb..e4622558af55 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -86,9 +86,6 @@ SoCs:
- MSM8998
compatible = "qcom,msm8998"
-- MSM8998_9x55
- compatible = "qcom,msm8998-9x55"
-
- MSMHAMSTER
compatible = "qcom,msmhamster"
@@ -110,6 +107,12 @@ SoCs:
- SDA630
compatible = "qcom,sda630"
+- SDM636
+ compatible = "qcom,sdm636"
+
+- SDA636
+ compatible = "qcom,sda636"
+
- MSM8952
compatible = "qcom,msm8952"
@@ -169,6 +172,9 @@ Generic board variants:
- RUMI device:
compatible = "qcom,rumi"
+- SVR device:
+ compatible = "qcom,svr"
+
Boards (SoC type + board variant):
@@ -202,6 +208,7 @@ compatible = "qcom,apqtitanium-mtp"
compatible = "qcom,apq8098-cdp"
compatible = "qcom,apq8098-mtp"
compatible = "qcom,apq8098-qrd"
+compatible = "qcom,apq8098-svr"
compatible = "qcom,mdm9630-cdp"
compatible = "qcom,mdm9630-mtp"
compatible = "qcom,mdm9630-sim"
@@ -273,8 +280,6 @@ compatible = "qcom,msm8998-rumi"
compatible = "qcom,msm8998-cdp"
compatible = "qcom,msm8998-mtp"
compatible = "qcom,msm8998-qrd"
-compatible = "qcom,msm8998-9x55-cdp"
-compatible = "qcom,msm8998-9x55-mtp"
compatible = "qcom,msmhamster-rumi"
compatible = "qcom,msmhamster-cdp"
compatible = "qcom,msmhamster-mtp"
@@ -291,6 +296,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/drm/msm/mdp.txt b/Documentation/devicetree/bindings/drm/msm/mdp.txt
index 3a6db0553fe3..a76b604445bd 100644
--- a/Documentation/devicetree/bindings/drm/msm/mdp.txt
+++ b/Documentation/devicetree/bindings/drm/msm/mdp.txt
@@ -3,6 +3,7 @@ Qualcomm Technologies,Inc. Adreno/Snapdragon display controller
Required properties:
Optional properties:
+- contiguous-region: reserved memory for HDMI and DSI buffer.
- qcom,sde-plane-id-map: plane id mapping for virtual plane.
- qcom,sde-plane-id: each virtual plane mapping node.
- reg: reg property.
@@ -17,6 +18,8 @@ Optional properties:
Example:
&mdss_mdp {
+ contiguous-region = <&cont_splash_mem &cont_splash_mem_hdmi>;
+
qcom,sde-plane-id-map {
qcom,sde-plane-id@0 {
reg = <0x0>;
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index d8c3a7c35465..80813dd1b3e5 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -93,6 +93,7 @@ Optional Properties:
- qcom,chipid: If it exists this property is used to replace
the chip identification read from the GPU hardware.
This is used to override faulty hardware readings.
+- qcom,disable-wake-on-touch: Boolean. Disables the GPU power up on a touch input event.
- qcom,disable-busy-time-burst:
Boolean. Disables the busy time burst to avoid switching
of power level for large frames based on the busy time limit.
@@ -141,6 +142,9 @@ Optional Properties:
rendering thread is running on masked CPUs.
Bit 0 is for CPU-0, bit 1 is for CPU-1...
+- qcom,l2pc-update-queue:
+ Disables L2PC on masked CPUs at queue time when it's true.
+
- qcom,snapshot-size:
Specify the size of snapshot in bytes. This will override
snapshot size defined in the driver code.
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
index 1e6aac56c44e..42e97f765bee 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
@@ -78,6 +78,8 @@ Optional properties for WLED:
- qcom,lcd-psm-ctrl : A boolean property to specify if PSM needs to be
controlled dynamically when WLED module is enabled
or disabled.
+- qcom,auto-calibration-enable : A boolean property which enables auto-calibration
+ of the WLED sink configuration.
Optional properties if 'qcom,disp-type-amoled' is mentioned in DT:
- qcom,loop-comp-res-kohm : control to select the compensation resistor in kohm. default is 320.
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index 9fb84020add7..bb413af4b54d 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -123,6 +123,9 @@ Optional properties:
- qcom,gpio-vdig : should contain index to gpio used by sensors digital vreg enable
- qcom,gpio-vaf : should contain index to gpio used by sensors af vreg enable
- qcom,gpio-af-pwdm : should contain index to gpio used by sensors af pwdm_n
+- qcom,gpio-custom1 : should contain index to gpio used by sensors specific to usecase
+- qcom,gpio-custom2 : should contain index to gpio used by sensors specific to usecase
+- qcom,gpio-custom3 : should contain index to gpio used by sensors specific to usecase
- qcom,gpio-req-tbl-num : should contain index to gpios specific to this sensor
- qcom,gpio-req-tbl-flags : should contain direction of gpios present in
qcom,gpio-req-tbl-num property (in the same order)
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index d00e26b4d5ed..9916c34e62b8 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -80,6 +80,7 @@ Optional Properties:
register dumps on CRC errors and also downgrade bus speed mode to
SDR50/DDR50 in case of continuous CRC errors. Set this flag to enable
this workaround.
+ - qcom,force-sdhc1-probe: Force probing sdhc1 even if it is not the boot device.
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
diff --git a/Documentation/devicetree/bindings/net/can/k61-can.txt b/Documentation/devicetree/bindings/net/can/k61-can.txt
new file mode 100644
index 000000000000..ea4a7b4ae035
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/k61-can.txt
@@ -0,0 +1,34 @@
+* Kinetis K61 CAN *
+
+This driver implements SPI slave protocol for Freescale K61 CAN controller.
+
+Required properties:
+ - compatible: Should be "fsl,k61" or "nxp,mpc5746c".
+ - reg: Should contain SPI chip select.
+ - interrupt-parent: Should specify interrupt controller for the interrupt.
+ - interrupts: Should contain IRQ line for the CAN controller.
+
+Optional properties:
+ - 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.
+ - bits-per-word: Indicate how many bits are in a SPI frame. e.g.: 8, 16, 32.
+ Default to 16.
+ - reset-delay-msec: Delay in milliseconds to be applied after resetting the chip.
+ Default to 1 ms.
+
+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>;
+ bits-per-word = <8>;
+ reset-delay-msec = <100>;
+ };
diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt
index fc019bda50a7..bf3ad8a71c26 100644
--- a/Documentation/devicetree/bindings/pci/msm_pcie.txt
+++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt
@@ -97,6 +97,9 @@ Optional Properties:
and assign for each endpoint.
- qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become
stable after power on, before de-assert the PERST to the endpoint.
+ - qcom,switch-latency: The time (unit: ms) to wait for the PCIe endpoint's link
+ training with switch downstream port after the link between switch upstream
+ port and RC is up.
- qcom,wr-halt-size: With base 2, this exponent determines the size of the
data that PCIe core will halt on for each write transaction.
- qcom,cpl-timeout: Completion timeout value. This value specifies the time range
@@ -276,6 +279,7 @@ Example:
qcom,smmu-exist;
qcom,smmu-sid-base = <0x1480>;
qcom,ep-latency = <100>;
+ qcom,switch-latency = <100>;
qcom,wr-halt-size = <0xa>; /* 1KB */
qcom,cpl-timeout = <0x2>;
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index 47a6fdd300ca..406920b7246e 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -88,6 +88,7 @@ Optional properties:
- qcom,override-acc-1: Override the default ACC settings with this value if present.
- qcom,cx-ipeak-vote: Boolean- Present if we need to set bit 5 of cxip_lm_vote_clear
during modem shutdown
+- qcom,minidump-id: Unique id for each subsystem
One child node to represent the MBA image may be specified, when the MBA image
needs to be loaded in a specifically carved out memory region.
diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt
index babc4523a29a..dd14890123e6 100644
--- a/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt
+++ b/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt
@@ -9,6 +9,8 @@ Required properties:
Optional property:
- qcom,fab-id-valid: Use this property when support to read Fab
identification from REV ID peripheral is available.
+- qcom,tp-rev-valid: Use this property when support to read TP
+ revision identification from REV ID peripheral.
Example:
qcom,revid@100 {
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-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/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
index 3527779ef93c..f01eae10bf4f 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
@@ -178,6 +178,10 @@ Charger specific properties:
Definition: WD bark-timeout in seconds. The possible values are
16, 32, 64, 128. If not defined it defaults to 64.
+- qcom,sw-jeita-enable
+ Usage: optional
+ Value type: bool
+ Definition: Boolean flag which when present enables sw compensation for jeita
=============================================
Second Level Nodes - SMB2 Charger Peripherals
diff --git a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
index f419655722d4..376af82381f2 100644
--- a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
+++ b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
@@ -11,6 +11,8 @@ Required properties:
Optional properties:
- qcom,fastrpc-glink: Flag to use glink instead of smd for IPC
+- qcom,fastrpc-vmid-heap-shared: Flag for Dynamic heap feature, to
+ share HLOS memory buffer to ADSP
Optional subnodes:
- qcom,msm_fastrpc_compute_cb : Child nodes representing the compute context
@@ -25,6 +27,7 @@ Example:
qcom,msm_fastrpc {
compatible = "qcom,msm-fastrpc-adsp";
qcom,fastrpc-glink;
+ qcom,fastrpc-vmid-heap-shared;
qcom,msm_fastrpc_compute_cb_1 {
compatible = "qcom,msm-fastrpc-compute-cb";
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
index ed383ce9ea8f..9798ac60b493 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
@@ -209,6 +209,12 @@ Properties below are specific to BOOST subnode only.
Definition: Current limit (in mA) of the BOOST rail.
Possible values are 200 to 1600mA in 200mA steps.
+- qcom,bst-headroom-mv
+ Usage: optional
+ Value type: <u16>
+ Definition: Headroom of the boost (in mV). The minimum headroom is
+ 200mV and if not specified defaults to 200mV.
+
=======
Example
=======
@@ -250,5 +256,6 @@ pm660l_lcdb: qpnp-lcdb@ec00 {
qcom,bst-pd-strength = <1>;
qcom,bst-ps = <1>;
qcom,bst-ps-threshold-ma = <50>;
+ qcom,bst-headroom-mv = <200>;
};
};
diff --git a/Documentation/devicetree/bindings/soc/qcom/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/soc/qcom/msm-early-cam.txt b/Documentation/devicetree/bindings/soc/qcom/msm-early-cam.txt
new file mode 100644
index 000000000000..388426f44524
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/msm-early-cam.txt
@@ -0,0 +1,110 @@
+* Qualcomm Technologies Inc MSM BA
+
+[Root level node]
+==================
+Required properties:
+- compatible: Must be "qcom,early-cam".
+
+[Subnode]
+==========
+- qcom,early-cam-input-profile-#: Defines child nodes for the profiles supported
+ by early camera driver. Each profile should have properties
+ "mmagic-supply", "gdscr-supply", "vfe0-vdd-supply",
+ "qcom,cam-vreg-name", "clocks", "clock-names",
+ "qcom,clock-rates".
+Required properties:
+- mmagic-supply : should contain mmagic regulator used for mmagic clocks.
+- gdscr-supply : should contain gdsr regulator used for cci clocks.
+- vfe0-vdd-supply: phandle to vfe0 regulator.
+- qcom,cam-vreg-name : name of the voltage regulators required for the device.
+- clocks: List of clock handles. The parent clocks of the input clocks to the
+ devices in this power domain are set to oscclk before power gating
+ and restored back after powering on a domain. This is required for
+ all domains which are powered on and off and not required for unused
+ domains.
+- clock-names: name of the clock used by the driver.
+- qcom,clock-rates: clock rate in Hz.
+Example:
+
+ qcom,early-cam {
+ cell-index = <0>;
+ compatible = "qcom,early-cam";
+ status = "ok";
+ mmagic-supply = <&gdsc_mmagic_camss>;
+ gdscr-supply = <&gdsc_camss_top>;
+ vfe0-vdd-supply = <&gdsc_vfe0>;
+ qcom,cam-vreg-name = "mmagic", "gdscr", "vfe0-vdd";
+ clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
+ <&clock_mmss clk_cci_clk_src>,
+ <&clock_mmss clk_camss_cci_ahb_clk>,
+ <&clock_mmss clk_camss_cci_clk>,
+ <&clock_mmss clk_camss_ahb_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>,
+ <&clock_mmss clk_camss_vfe_ahb_clk>,
+ <&clock_mmss clk_camss_vfe0_ahb_clk>,
+ <&clock_mmss clk_camss_vfe_axi_clk>,
+ <&clock_mmss clk_camss_vfe0_stream_clk>,
+ <&clock_mmss clk_smmu_vfe_axi_clk>,
+ <&clock_mmss clk_smmu_vfe_ahb_clk>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_vfe0_clk_src>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_camss_csi2_ahb_clk>,
+ <&clock_mmss clk_camss_csi2_clk>,
+ <&clock_mmss clk_camss_csi2phy_clk>,
+ <&clock_mmss clk_csi2phytimer_clk_src>,
+ <&clock_mmss clk_camss_csi2phytimer_clk>,
+ <&clock_mmss clk_camss_csi2rdi_clk>,
+ <&clock_mmss clk_camss_ispif_ahb_clk>,
+ <&clock_mmss clk_camss_vfe0_clk>;
+ clock-names =
+ "mmss_mmagic_ahb_clk",
+ "camss_top_ahb_clk",
+ "cci_clk_src",
+ "camss_cci_ahb_clk",
+ "camss_cci_clk",
+ "camss_ahb_clk",
+ "mmagic_camss_axi_clk",
+ "camss_vfe_ahb_clk",
+ "camss_vfe0_ahb_clk",
+ "camss_vfe_axi_clk",
+ "camss_vfe0_stream_clk",
+ "smmu_vfe_axi_clk",
+ "smmu_vfe_ahb_clk",
+ "camss_csi_vfe0_clk",
+ "vfe0_clk_src",
+ "camss_csi_vfe0_clk",
+ "camss_csi2_ahb_clk",
+ "camss_csi2_clk",
+ "camss_csi2phy_clk",
+ "csi2phytimer_clk_src",
+ "camss_csi2phytimer_clk",
+ "camss_csi2rdi_clk",
+ "camss_ispif_ahb_clk",
+ "clk_camss_vfe0_clk";
+
+ qcom,clock-rates = <19200000
+ 19200000
+ 19200000
+ 19200000
+ 19200000
+ 19200000
+ 0
+ 0
+ 0
+ 320000000
+ 0
+ 0
+ 0
+ 0
+ 19200000
+ 0
+ 0
+ 200000000
+ 200000000
+ 200000000
+ 200000000
+ 200000000
+ 0
+ };
diff --git a/Documentation/devicetree/bindings/spi/spidev.txt b/Documentation/devicetree/bindings/spi/spidev.txt
new file mode 100644
index 000000000000..886eb4bfadf5
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spidev.txt
@@ -0,0 +1,30 @@
+SPIDEV
+
+SPI slave devices using the spidev driver allowing for userspace
+control of the SPI devices. Must be children of a SPI master node
+and contain the following properties.
+
+Required properties:
+- compatible: Should contain:
+ "nxp,mpc57xx" for external CAN controller
+ "infineon,sli97" for external HSM module
+
+- reg: Chip select address of device.
+- spi-max-frequency: Maximum SPI clocking speed of device in Hz.
+
+Optional properties:
+- spi-cpol: Empty property indicating device requires
+ inverse clock polarity (CPOL) mode.
+- spi-cpha: Empty property indicating device requires
+ shifted clock phase (CPHA) mode.
+
+Other optional properties described in
+Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Example:
+
+ spi@0 {
+ compatible = "nxp,mpc57xx";
+ reg = <0>;
+ spi-max-frequency = <19200000>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt
index 88ad24900f28..9b40d44d363b 100644
--- a/Documentation/devicetree/bindings/usb/msm-phy.txt
+++ b/Documentation/devicetree/bindings/usb/msm-phy.txt
@@ -214,6 +214,8 @@ Optional properties:
- qcom,major-rev: provide major revision number to differentiate power up sequence. default is 2.0
- qcom,vdda33-voltage-level: A list of three integer values (min, op, max) representing
specific voltages (in microvolts) used for the vdda33 supply.
+ - qcom,tune2-efuse-correction: The value to be adjusted from fused value for
+ improved rise/fall times.
Example:
qusb_phy: qusb@f9b39000 {
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 91412a10bf65..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/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt b/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt
index 061c1d16ad24..d0855115b6d1 100644
--- a/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt
+++ b/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt
@@ -12,7 +12,7 @@ Required properties:
"riva_ccu_base", "pronto_a2xb_base", "pronto_ccpu_base",
"pronto_saw2_base", "wlan_tx_phy_aborts","wlan_brdg_err_source",
"wlan_tx_status", "alarms_txctl", "alarms_tactl",
- "pronto_mcu_base".
+ "pronto_mcu_base", "pronto_qfuse".
- interupts: Pronto to Apps interrupts for tx done and rx pending.
- qcom,pronto-vddmx-supply: regulator to supply pronto pll.
- qcom,pronto-vddcx-supply: voltage corner regulator to supply WLAN/BT/FM
@@ -29,7 +29,7 @@ Required properties:
- qcom,wcnss-vadc: VADC handle for battery voltage notification APIs.
- pinctrl-<n> : Pinctrl states as described in bindings/pinctrl/pinctrl-bindings.txt
- pinctrl-names : Names corresponding to the numbered pinctrl states
-- clocks: from common clock binding: handle to xo and rf_clk clocks.
+- clocks: from common clock binding: handle to xo, rf_clk and wcnss snoc clocks.
- clock-names: Names of all the clocks that are accessed by the subsystem
- qcom,vdd-voltage-level: This property represents (nominal, min, max) voltage
for iris and pronto regulators in milli-volts.
@@ -39,11 +39,16 @@ iris and pronto regulators in micro-amps.
Optional properties:
- qcom,has-autodetect-xo: boolean flag to determine whether Iris XO auto detect
should be performed during boot up.
+- qcom,snoc-wcnss-clock-freq: indicates the wcnss snoc clock frequency in Hz.
+If wcnss_snoc clock is specified in the list of clocks, this property needs
+to be set to make it functional.
- qcom,wlan-rx-buff-count: WLAN RX buffer count is a configurable value,
using a smaller count for this buffer will reduce the memory usage.
- qcom,is-pronto-v3: boolean flag to determine the pronto hardware version
in use. subsequently correct workqueue will be used by DXE engine to push frames
in TX data path.
+- qcom,is-dual-band-disable: boolean flag to determine the WLAN dual band
+ capability.
- qcom,is-pronto-vadc: boolean flag to determine Battery voltage feature
support for pronto hardware.
- qcom,wcnss-pm : <Core rail LDO#, PA rail LDO#, XO settling time,
@@ -94,7 +99,12 @@ Example:
clocks = <&clock_rpm clk_xo_wlan_clk>,
<&clock_rpm clk_rf_clk2>,
<&clock_debug clk_gcc_debug_mux>,
- <&clock_gcc clk_wcnss_m_clk>;
- clock-names = "xo", "rf_clk", "measure", "wcnss_debug";
+ <&clock_gcc clk_wcnss_m_clk>,
+ <&clock_gcc clk_snoc_wcnss_a_clk>;
+
+ clock-names = "xo", "rf_clk", "measure", "wcnss_debug",
+ "snoc_wcnss";
+
+ qcom,snoc-wcnss-clock-freq = <200000000>;
qcom,wcnss-pm = <11 21 1200 1 1 6>;
};
diff --git a/Makefile b/Makefile
index 64e9303560f7..919851a86eaa 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 4
-SUBLEVEL = 76
+SUBLEVEL = 85
EXTRAVERSION =
NAME = Blurry Fish Butt
@@ -623,6 +623,9 @@ include arch/$(SRCARCH)/Makefile
KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,)
KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,)
KBUILD_CFLAGS += $(call cc-disable-warning,frame-address,)
+KBUILD_CFLAGS += $(call cc-disable-warning, format-truncation)
+KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow)
+KBUILD_CFLAGS += $(call cc-disable-warning, int-in-bool-context)
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os
diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg
index 96edb3c84425..419dca62542a 100644
--- a/android/configs/android-base.cfg
+++ b/android/configs/android-base.cfg
@@ -17,7 +17,6 @@ CONFIG_AUDIT=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_CGROUPS=y
CONFIG_CGROUP_CPUACCT=y
-CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_SCHED=y
CONFIG_DEFAULT_SECURITY_SELINUX=y
@@ -140,11 +139,6 @@ CONFIG_PPP_DEFLATE=y
CONFIG_PPP_MPPE=y
CONFIG_PREEMPT=y
CONFIG_PROFILING=y
-CONFIG_QFMT_V2=y
-CONFIG_QUOTA=y
-CONFIG_QUOTA_NETLINK_INTERFACE=y
-CONFIG_QUOTA_TREE=y
-CONFIG_QUOTACTL=y
CONFIG_RANDOMIZE_BASE=y
CONFIG_RTC_CLASS=y
CONFIG_RT_GROUP_SCHED=y
diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg
index 11df0892301b..6550d0423f50 100644
--- a/android/configs/android-recommended.cfg
+++ b/android/configs/android-recommended.cfg
@@ -110,6 +110,11 @@ CONFIG_POWER_SUPPLY=y
CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_RAM=y
+CONFIG_QFMT_V2=y
+CONFIG_QUOTA=y
+CONFIG_QUOTACTL=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QUOTA_TREE=y
CONFIG_SCHEDSTATS=y
CONFIG_SMARTJOYPLUS_FF=y
CONFIG_SND=y
diff --git a/arch/Kconfig b/arch/Kconfig
index 98f64ad1caf1..ed2539c590bf 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -225,8 +225,8 @@ config ARCH_INIT_TASK
config ARCH_TASK_STRUCT_ALLOCATOR
bool
-# Select if arch has its private alloc_thread_info() function
-config ARCH_THREAD_INFO_ALLOCATOR
+# Select if arch has its private alloc_thread_stack() function
+config ARCH_THREAD_STACK_ALLOCATOR
bool
# Select if arch wants to size task_struct dynamically via arch_task_struct_size:
diff --git a/arch/arc/include/asm/cache.h b/arch/arc/include/asm/cache.h
index 210ef3e72332..0ddd7144c492 100644
--- a/arch/arc/include/asm/cache.h
+++ b/arch/arc/include/asm/cache.h
@@ -88,7 +88,9 @@ extern int ioc_exists;
#define ARC_REG_SLC_FLUSH 0x904
#define ARC_REG_SLC_INVALIDATE 0x905
#define ARC_REG_SLC_RGN_START 0x914
+#define ARC_REG_SLC_RGN_START1 0x915
#define ARC_REG_SLC_RGN_END 0x916
+#define ARC_REG_SLC_RGN_END1 0x917
/* Bit val in SLC_CONTROL */
#define SLC_CTRL_IM 0x040
diff --git a/arch/arc/mm/cache.c b/arch/arc/mm/cache.c
index d81b6d7e11e7..9a84cbdd44b0 100644
--- a/arch/arc/mm/cache.c
+++ b/arch/arc/mm/cache.c
@@ -543,6 +543,7 @@ noinline void slc_op(phys_addr_t paddr, unsigned long sz, const int op)
static DEFINE_SPINLOCK(lock);
unsigned long flags;
unsigned int ctrl;
+ phys_addr_t end;
spin_lock_irqsave(&lock, flags);
@@ -572,8 +573,16 @@ noinline void slc_op(phys_addr_t paddr, unsigned long sz, const int op)
* END needs to be setup before START (latter triggers the operation)
* END can't be same as START, so add (l2_line_sz - 1) to sz
*/
- write_aux_reg(ARC_REG_SLC_RGN_END, (paddr + sz + l2_line_sz - 1));
- write_aux_reg(ARC_REG_SLC_RGN_START, paddr);
+ end = paddr + sz + l2_line_sz - 1;
+ if (is_pae40_enabled())
+ write_aux_reg(ARC_REG_SLC_RGN_END1, upper_32_bits(end));
+
+ write_aux_reg(ARC_REG_SLC_RGN_END, lower_32_bits(end));
+
+ if (is_pae40_enabled())
+ write_aux_reg(ARC_REG_SLC_RGN_START1, upper_32_bits(paddr));
+
+ write_aux_reg(ARC_REG_SLC_RGN_START, lower_32_bits(paddr));
while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY);
diff --git a/arch/arm/boot/dts/armada-388-gp.dts b/arch/arm/boot/dts/armada-388-gp.dts
index cd316021d6ce..6c1b45c1af66 100644
--- a/arch/arm/boot/dts/armada-388-gp.dts
+++ b/arch/arm/boot/dts/armada-388-gp.dts
@@ -89,7 +89,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pca0_pins>;
interrupt-parent = <&gpio0>;
- interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
+ interrupts = <18 IRQ_TYPE_LEVEL_LOW>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -101,7 +101,7 @@
compatible = "nxp,pca9555";
pinctrl-names = "default";
interrupt-parent = <&gpio0>;
- interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
+ interrupts = <18 IRQ_TYPE_LEVEL_LOW>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts
index 5f5e0f3d5b64..27cd4abfc74d 100644
--- a/arch/arm/boot/dts/omap3-n900.dts
+++ b/arch/arm/boot/dts/omap3-n900.dts
@@ -697,6 +697,8 @@
vmmc_aux-supply = <&vsim>;
bus-width = <8>;
non-removable;
+ no-sdio;
+ no-sd;
};
&mmc3 {
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index 297d6535382e..c944ecfa4eeb 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -32,6 +32,7 @@ dtb-$(CONFIG_ARCH_MSM8996) += msm8996-v2-pmi8994-cdp.dtb \
msm8996pro-auto-adp.dtb \
msm8996pro-auto-adp-lite.dtb \
msm8996pro-auto-cdp.dtb \
+ msm8996pro-auto-cv2x.dtb \
msm8996pro-pmi8994-cdp.dtb \
msm8996pro-pmi8994-mtp.dtb \
msm8996pro-pmi8994-pmk8001-cdp.dtb \
@@ -168,12 +169,10 @@ dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \
apq8098-v2.1-cdp.dtb \
apq8098-v2.1-qrd.dtb \
apq8098-v2.1-mediabox.dtb \
+ apq8098-v2.1-svr20.dtb \
msm8998-v2.1-interposer-sdm660-cdp.dtb \
msm8998-v2.1-interposer-sdm660-mtp.dtb \
- msm8998-v2.1-interposer-sdm660-qrd.dtb \
- msm8998-9x55-rcm.dtb \
- msm8998-9x55-cdp.dtb \
- msm8998-9x55-mtp.dtb
+ msm8998-v2.1-interposer-sdm660-qrd.dtb
endif
dtb-$(CONFIG_ARCH_MSMHAMSTER) += msmhamster-rumi.dtb
@@ -228,7 +227,34 @@ dtb-$(CONFIG_ARCH_SDM660) += sdm660-sim.dtb \
sda658-rcm.dtb \
sda658-pm660a-mtp.dtb \
sda658-pm660a-cdp.dtb \
- sda658-pm660a-rcm.dtb
+ sda658-pm660a-rcm.dtb \
+ sdm636-cdp.dtb \
+ sdm636-mtp.dtb \
+ sdm636-qrd.dtb \
+ sdm636-rcm.dtb \
+ sdm636-headset-jacktype-no-cdp.dtb \
+ sdm636-headset-jacktype-no-rcm.dtb \
+ sdm636-internal-codec-cdp.dtb \
+ sdm636-internal-codec-mtp.dtb \
+ sdm636-internal-codec-pm660a-cdp.dtb \
+ sdm636-internal-codec-pm660a-mtp.dtb \
+ sdm636-internal-codec-pm660a-rcm.dtb \
+ sdm636-internal-codec-rcm.dtb \
+ sdm636-pm660a-headset-jacktype-no-cdp.dtb \
+ sdm636-pm660a-headset-jacktype-no-rcm.dtb \
+ sdm636-pm660a-cdp.dtb \
+ sdm636-pm660a-mtp.dtb \
+ sdm636-pm660a-qrd.dtb \
+ sdm636-pm660a-rcm.dtb \
+ sdm636-usbc-audio-mtp.dtb \
+ sdm636-usbc-audio-rcm.dtb \
+ sda636-cdp.dtb \
+ sda636-mtp.dtb \
+ sda636-rcm.dtb \
+ sda636-pm660a-cdp.dtb \
+ sda636-pm660a-mtp.dtb \
+ sda636-pm660a-qrd-hdk.dtb \
+ sda636-pm660a-rcm.dtb
dtb-$(CONFIG_ARCH_SDM630) += sdm630-rumi.dtb \
sdm630-pm660a-rumi.dtb \
diff --git a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi
index 4081a21b3134..db33594d3827 100644
--- a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi
@@ -713,6 +713,10 @@
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
<&incall_music_rx>, <&incall_music2_rx>,
+ <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>,
+ <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>,
+ <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>,
+ <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>,
<&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>,
<&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>,
<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
@@ -731,6 +735,10 @@
"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+ "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867",
+ "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871",
+ "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866",
+ "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870",
"msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883",
"msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887",
"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
index 9f8d432a3112..082b04791dbd 100644
--- a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
+++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -42,6 +42,10 @@
i2c@75b6000 { /* BLSP8 */
/* ADV7533 HDMI Bridge Chip removed on ADP Lite */
+ adv7533@3d {
+ status = "disabled";
+ };
+
adv7533@39 {
status = "disabled";
};
@@ -60,6 +64,10 @@
/delete-property/ qcom,dsi-display-active;
};
+&sde_kms {
+ connectors = <&sde_hdmi_tx &sde_hdmi &dsi_adv_7533_1>;
+};
+
&pil_modem {
pinctrl-names = "default";
pinctrl-0 = <&modem_mux>;
diff --git a/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts
index bd29c0307576..1fa49d8a060d 100644
--- a/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts
@@ -101,6 +101,10 @@
status = "disabled";
};
+&pcie0 {
+ qcom,boot-option = <0x0>;
+};
+
&soc {
qcom,msm-dai-mi2s {
dai_mi2s3: qcom,msm-dai-q6-mi2s-quat {
diff --git a/arch/arm/boot/dts/qcom/msm8998-9x55-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-svr20.dts
index cf167897bb89..4359a3f38ade 100644
--- a/arch/arm/boot/dts/qcom/msm8998-9x55-cdp.dts
+++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-svr20.dts
@@ -1,5 +1,4 @@
-/*
- * Copyright (c) 2017, 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
@@ -13,12 +12,11 @@
/dts-v1/;
-#include "msm8998-9x55.dtsi"
-#include "msm8998-mdss-panels.dtsi"
-#include "msm8998-cdp.dtsi"
+#include "apq8098-v2.1.dtsi"
+#include "msm8998-svr20.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. MSM8998-9x55 CDP";
- compatible = "qcom,msm8998-9x55-cdp", "qcom,msm8998-9x55", "qcom,cdp";
- qcom,board-id= <1 2>;
+ model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 SVR V2.0 Board";
+ compatible = "qcom,apq8098-svr", "qcom,apq8098", "qcom,svr";
+ qcom,board-id = <0x03020008 3>;
};
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
index 5971a3d1025e..aa627b3e7c63 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
@@ -68,7 +68,6 @@
qcom,mdss-dsi-lane-1-state;
qcom,mdss-dsi-lane-2-state;
qcom,mdss-dsi-lane-3-state;
- qcom,cmd-sync-wait-broadcast;
qcom,mdss-dsi-panel-timings = [e2 36 24 00 66 6a 28 38 2a 03 04 00];
qcom,mdss-dsi-t-clk-post = <0x0d>;
qcom,mdss-dsi-t-clk-pre = <0x2d>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi
index 1a572f97c840..339d87f66d2f 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi
@@ -193,7 +193,6 @@
qcom,mdss-dsi-lane-1-state;
qcom,mdss-dsi-lane-2-state;
qcom,mdss-dsi-lane-3-state;
- qcom,cmd-sync-wait-broadcast;
qcom,mdss-dsi-panel-timings = [e2 36 24 00 66 6a 28 38 2a 03
04 00];
qcom,mdss-dsi-t-clk-post = <0x0d>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi
index 51a225b82f47..ff3b7b80c449 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -54,6 +54,8 @@
qcom,ulps-enabled;
qcom,dcs-cmd-by-left;
qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-pan-physical-width-dimension = <68>;
+ qcom,mdss-pan-physical-height-dimension = <121>;
qcom,adjust-timer-wakeup-ms = <1>;
qcom,mdss-dsi-on-command = [
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi
index 02c87067f212..933746b8abe7 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,6 +46,8 @@
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 20>;
qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-pan-physical-width-dimension = <68>;
+ qcom,mdss-pan-physical-height-dimension = <121>;
qcom,adjust-timer-wakeup-ms = <1>;
qcom,mdss-dsi-on-command = [
diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-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/fg-gen3-batterydata-svr-v2-3200mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-svr-v2-3200mah.dtsi
new file mode 100644
index 000000000000..048897b084ec
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-svr-v2-3200mah.dtsi
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+qcom,svr835v2_3200mah {
+ /*3003021_TC_MLP603170_3200mAh_averaged_MasterSlave_Jun292017*/
+ qcom, = <24>;
+ qcom,max-voltage-uv = <4200000>;
+ qcom,fg-cc-cv-threshold-mv = <4190>;
+ qcom,fastchg-current-ma = <3200>;
+ qcom,nom-batt-capacity-mah = <3200>;
+ qcom,batt-id-kohm = <0>;
+ qcom,battery-beta = <3435>;
+ qcom,battery-type = "svr835v2_3200mah";
+ qcom,checksum = <0xB7B0>;
+ qcom,gui-version = "PMI8998GUI - 2.0.0.58";
+ qcom,fg-profile-data = [
+ 87 16 AB 0B
+ BE 15 3A 0A
+ 8B 1C 6D 02
+ 76 0D 1F 0A
+ 50 18 ED 22
+ 98 45 CA 52
+ 83 00 00 00
+ 0D 00 00 00
+ 00 00 37 B4
+ 78 C5 9D BA
+ 29 00 08 00
+ 3E CA 11 E5
+ D4 06 B7 EA
+ 51 07 0F 02
+ 82 DD 22 3B
+ 1C 06 09 20
+ 27 00 14 00
+ 1C 19 82 0A
+ E9 0C 49 03
+ 84 1C 5C 03
+ D0 15 0D 12
+ 91 19 0C 22
+ F0 3C 35 4B
+ 7D 00 00 00
+ 12 00 00 00
+ 00 00 F3 D4
+ 9F B4 AF D3
+ 22 00 00 00
+ CC EA 11 E5
+ 2D F4 35 E3
+ A5 F3 49 0B
+ 8F EA 5A 1A
+ 9B 33 CC FF
+ 07 10 00 00
+ 21 0D 33 43
+ 22 00 40 00
+ 07 01 0A FA
+ FF 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ ];
+};
diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi
index 93aeef07cfe0..460e7e76ac4d 100644
--- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi
@@ -24,6 +24,7 @@
compatible = "qcom,qpnp-revid";
reg = <0x100 0x100>;
qcom,fab-id-valid;
+ qcom,tp-rev-valid;
};
pm660_misc: qcom,misc@900 {
diff --git a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
index 9cd117ce4e0c..075eaef21254 100644
--- a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
@@ -269,6 +269,7 @@
qcom,led-strings-list = [00 01 02];
qcom,loop-auto-gm-en;
qcom,pmic-revid = <&pm660l_revid>;
+ qcom,auto-calibration-enable;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi
index 684f6cf9b389..147b537eba33 100644
--- a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi
@@ -634,6 +634,7 @@
qcom,en-ext-pfet-sc-pro;
qcom,pmic-revid = <&pmi8998_revid>;
qcom,loop-auto-gm-en;
+ qcom,auto-calibration-enable;
status = "okay";
};
diff --git a/arch/arm/boot/dts/qcom/msm-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-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
index 9dbec1e87fda..1283cdddc2db 100644
--- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
@@ -858,6 +858,90 @@
};
&soc {
+ qcom,early-cam {
+ cell-index = <0>;
+ compatible = "qcom,early-cam";
+ status = "ok";
+ mmagic-supply = <&gdsc_mmagic_camss>;
+ gdscr-supply = <&gdsc_camss_top>;
+ vfe0-vdd-supply = <&gdsc_vfe0>;
+ qcom,cam-vreg-name = "mmagic", "gdscr", "vfe0-vdd";
+ clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
+ <&clock_mmss clk_cci_clk_src>,
+ <&clock_mmss clk_camss_cci_ahb_clk>,
+ <&clock_mmss clk_camss_cci_clk>,
+ <&clock_mmss clk_camss_ahb_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>,
+ <&clock_mmss clk_camss_vfe_ahb_clk>,
+ <&clock_mmss clk_camss_vfe0_ahb_clk>,
+ <&clock_mmss clk_camss_vfe_axi_clk>,
+ <&clock_mmss clk_camss_vfe0_stream_clk>,
+ <&clock_mmss clk_smmu_vfe_axi_clk>,
+ <&clock_mmss clk_smmu_vfe_ahb_clk>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_vfe0_clk_src>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_camss_csi2_ahb_clk>,
+ <&clock_mmss clk_camss_csi2_clk>,
+ <&clock_mmss clk_camss_csi2phy_clk>,
+ <&clock_mmss clk_csi2phytimer_clk_src>,
+ <&clock_mmss clk_camss_csi2phytimer_clk>,
+ <&clock_mmss clk_camss_csi2rdi_clk>,
+ <&clock_mmss clk_camss_ispif_ahb_clk>,
+ <&clock_mmss clk_camss_vfe0_clk>;
+ clock-names =
+ "mmss_mmagic_ahb_clk",
+ "camss_top_ahb_clk",
+ "cci_clk_src",
+ "camss_cci_ahb_clk",
+ "camss_cci_clk",
+ "camss_ahb_clk",
+ "mmagic_camss_axi_clk",
+ "camss_vfe_ahb_clk",
+ "camss_vfe0_ahb_clk",
+ "camss_vfe_axi_clk",
+ "camss_vfe0_stream_clk",
+ "smmu_vfe_axi_clk",
+ "smmu_vfe_ahb_clk",
+ "camss_csi_vfe0_clk",
+ "vfe0_clk_src",
+ "camss_csi_vfe0_clk",
+ "camss_csi2_ahb_clk",
+ "camss_csi2_clk",
+ "camss_csi2phy_clk",
+ "csi2phytimer_clk_src",
+ "camss_csi2phytimer_clk",
+ "camss_csi2rdi_clk",
+ "camss_ispif_ahb_clk",
+ "clk_camss_vfe0_clk";
+
+ qcom,clock-rates = <19200000
+ 19200000
+ 19200000
+ 19200000
+ 19200000
+ 19200000
+ 0
+ 0
+ 0
+ 320000000
+ 0
+ 0
+ 0
+ 0
+ 19200000
+ 0
+ 0
+ 200000000
+ 200000000
+ 200000000
+ 200000000
+ 200000000
+ 0
+ 100000000>;
+ };
+
qcom,ntn_avb {
compatible = "qcom,ntn_avb";
@@ -1016,6 +1100,10 @@
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
<&incall_music_rx>, <&incall_music2_rx>,
+ <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>,
+ <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>,
+ <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>,
+ <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>,
<&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>,
<&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>,
<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
@@ -1034,6 +1122,10 @@
"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+ "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867",
+ "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871",
+ "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866",
+ "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870",
"msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883",
"msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887",
"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
@@ -1449,11 +1541,21 @@
};
};
+&vfe_smmu {
+ qcom,no-smr-check;
+};
+
/ {
reserved-memory {
- lk_mem: lk_pool@91600000 {
- reg = <0x0 0x91600000 0x0 0x600000>;
+ lk_mem: lk_pool@0x91600000 {
+ no-map;
+ reg = <0 0x91600000 0 0x00600000>;
label = "lk_pool";
};
+
+ early_camera_mem: early_camera_mem@b3fff000 {
+ reg = <0 0xb3fff000 0 0x800000>;
+ label = "early_camera_mem";
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
index ad14bfd00cd1..c3b986786034 100644
--- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
@@ -623,6 +623,90 @@
};
&soc {
+ qcom,early-cam {
+ cell-index = <0>;
+ compatible = "qcom,early-cam";
+ status = "ok";
+ mmagic-supply = <&gdsc_mmagic_camss>;
+ gdscr-supply = <&gdsc_camss_top>;
+ vfe0-vdd-supply = <&gdsc_vfe0>;
+ qcom,cam-vreg-name = "mmagic", "gdscr", "vfe0-vdd";
+ clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
+ <&clock_mmss clk_cci_clk_src>,
+ <&clock_mmss clk_camss_cci_ahb_clk>,
+ <&clock_mmss clk_camss_cci_clk>,
+ <&clock_mmss clk_camss_ahb_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>,
+ <&clock_mmss clk_camss_vfe_ahb_clk>,
+ <&clock_mmss clk_camss_vfe0_ahb_clk>,
+ <&clock_mmss clk_camss_vfe_axi_clk>,
+ <&clock_mmss clk_camss_vfe0_stream_clk>,
+ <&clock_mmss clk_smmu_vfe_axi_clk>,
+ <&clock_mmss clk_smmu_vfe_ahb_clk>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_vfe0_clk_src>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_camss_csi2_ahb_clk>,
+ <&clock_mmss clk_camss_csi2_clk>,
+ <&clock_mmss clk_camss_csi2phy_clk>,
+ <&clock_mmss clk_csi2phytimer_clk_src>,
+ <&clock_mmss clk_camss_csi2phytimer_clk>,
+ <&clock_mmss clk_camss_csi2rdi_clk>,
+ <&clock_mmss clk_camss_ispif_ahb_clk>,
+ <&clock_mmss clk_camss_vfe0_clk>;
+ clock-names =
+ "mmss_mmagic_ahb_clk",
+ "camss_top_ahb_clk",
+ "cci_clk_src",
+ "camss_cci_ahb_clk",
+ "camss_cci_clk",
+ "camss_ahb_clk",
+ "mmagic_camss_axi_clk",
+ "camss_vfe_ahb_clk",
+ "camss_vfe0_ahb_clk",
+ "camss_vfe_axi_clk",
+ "camss_vfe0_stream_clk",
+ "smmu_vfe_axi_clk",
+ "smmu_vfe_ahb_clk",
+ "camss_csi_vfe0_clk",
+ "vfe0_clk_src",
+ "camss_csi_vfe0_clk",
+ "camss_csi2_ahb_clk",
+ "camss_csi2_clk",
+ "camss_csi2phy_clk",
+ "csi2phytimer_clk_src",
+ "camss_csi2phytimer_clk",
+ "camss_csi2rdi_clk",
+ "camss_ispif_ahb_clk",
+ "clk_camss_vfe0_clk";
+
+ qcom,clock-rates = <19200000
+ 19200000
+ 19200000
+ 19200000
+ 19200000
+ 19200000
+ 0
+ 0
+ 0
+ 320000000
+ 0
+ 0
+ 0
+ 0
+ 19200000
+ 0
+ 0
+ 200000000
+ 200000000
+ 200000000
+ 200000000
+ 200000000
+ 0
+ 100000000>;
+ };
+
ntn1: ntn_avb@1 { /* Neutrno device on RC1*/
compatible = "qcom,ntn_avb";
@@ -838,6 +922,10 @@
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
<&incall_music_rx>, <&incall_music2_rx>,
+ <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>,
+ <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>,
+ <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>,
+ <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>,
<&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>,
<&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>,
<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
@@ -856,6 +944,10 @@
"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+ "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867",
+ "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871",
+ "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866",
+ "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870",
"msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883",
"msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887",
"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
@@ -1223,12 +1315,22 @@
/delete-property/ qcom,spkr-sd-n-gpio;
};
+&vfe_smmu {
+ qcom,no-smr-check;
+};
+
/ {
reserved-memory {
- lk_mem: lk_pool@91600000 {
- reg = <0x0 0x91600000 0x0 0x600000>;
+ lk_mem: lk_pool@0x91600000 {
+ no-map;
+ reg = <0 0x91600000 0 0x00600000>;
label = "lk_pool";
};
+
+ early_camera_mem: early_camera_mem@b3fff000 {
+ reg = <0 0xb3fff000 0 0x800000>;
+ label = "early_camera_mem";
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/msm8996-cv2x.dtsi b/arch/arm/boot/dts/qcom/msm8996-cv2x.dtsi
new file mode 100644
index 000000000000..f1cf3136dbd0
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8996-cv2x.dtsi
@@ -0,0 +1,650 @@
+/* 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";
+ };
+
+ gpio_fan {
+ /* Based on 5v 75mA MC30100V2 */
+ compatible = "gpio-fan";
+ gpios = <&tlmm 43 GPIO_ACTIVE_LOW>;
+ gpio-fan,speed-map = <0 1>,
+ <8000 0>;
+ };
+};
+
+&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@0 {
+ compatible = "nxp,mpc5746c";
+ reg = <0>;
+ spi-max-frequency = <9600000>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <78 0>;
+ reset-gpio = <&tlmm 71 GPIO_ACTIVE_LOW>;
+ bits-per-word = <8>;
+ reset-delay-msec = <100>;
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&can_rst_on>;
+ pinctrl-1 = <&can_rst_off>;
+ };
+};
+
+&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";
+};
+
+&pm8994_gpios {
+ gpio@c700 { /* GPIO 8 - WLAN_EN */
+ qcom,mode = <1>; /* Digital output*/
+ qcom,pull = <4>; /* Pulldown 10uA */
+ qcom,vin-sel = <2>; /* VIN2 */
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,invert = <0>; /* Invert */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+
+ gpio@c800 { /* GPIO 9 - Rome 3.3V control */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* MOS logic */
+ qcom,invert = <1>; /* Output high */
+ qcom,vin-sel = <0>; /* VPH_PWR */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,out-strength = <1>; /* High drive strength */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+
+ gpio@ca00 { /* GPIO 11 - USB ENB1 (otg switch) */
+ qcom,mode = <1>; /* DIGITAL OUT */
+ qcom,vin-sel = <2>; /* 1.8 */
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+
+ gpio@d000 { /* GPIO 17 - USB1 VBUS detect */
+ qcom,mode = <0>; /* Digital Input*/
+ qcom,pull = <5>; /* No pull */
+ qcom,vin-sel = <2>; /* 1.8 V */
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+
+ gpio@d100 { /* GPIO 18 - Rome Sleep Clock */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,invert = <0>; /* Output low initially */
+ qcom,vin-sel = <2>; /* VIN 2 */
+ qcom,src-sel = <3>; /* Function 2 */
+ qcom,out-strength = <2>; /* Medium */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+};
+
+&pm8994_mpps {
+ mpp@a100 { /* MPP 2 */
+ /* MDM PON conrol*/
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,vin-sel = <2>; /* S4 1.8V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
+ qcom,invert = <1>; /* Output high */
+ status = "okay";
+ };
+};
+
+&pm8994_vadc {
+ chan@5 {
+ label = "vcoin";
+ reg = <5>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@7 {
+ label = "vph_pwr";
+ reg = <7>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@73 {
+ label = "msm_therm";
+ reg = <0x73>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@74 {
+ label = "emmc_therm";
+ reg = <0x74>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@75 {
+ label = "pa_therm0";
+ reg = <0x75>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@77 {
+ label = "pa_therm1";
+ reg = <0x77>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@78 {
+ label = "quiet_therm";
+ reg = <0x78>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@7c {
+ label = "xo_therm_buf";
+ reg = <0x7c>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+};
+
+&pm8994_adc_tm {
+ chan@73 {
+ label = "msm_therm";
+ reg = <0x73>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x48>;
+ qcom,thermal-node;
+ };
+
+ chan@74 {
+ label = "emmc_therm";
+ reg = <0x74>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x68>;
+ qcom,thermal-node;
+ };
+
+ chan@75 {
+ label = "pa_therm0";
+ reg = <0x75>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x70>;
+ qcom,thermal-node;
+ };
+
+ chan@77 {
+ label = "pa_therm1";
+ reg = <0x77>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x78>;
+ qcom,thermal-node;
+ };
+
+ chan@78 {
+ label = "quiet_therm";
+ reg = <0x78>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x80>;
+ qcom,thermal-node;
+ };
+
+ chan@7c {
+ label = "xo_therm_buf";
+ reg = <0x7c>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x88>;
+ qcom,thermal-node;
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi
index d3ea51268590..c8898ec01992 100644
--- a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi
@@ -538,6 +538,10 @@
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
<&incall_music_rx>, <&incall_music2_rx>,
+ <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>,
+ <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>,
+ <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>,
+ <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>,
<&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>,
<&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>,
<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
@@ -556,6 +560,10 @@
"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+ "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867",
+ "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871",
+ "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866",
+ "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870",
"msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883",
"msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887",
"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
diff --git a/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi
index ff128acb376a..244901bd5cef 100644
--- a/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1449,7 +1449,7 @@
};
cnss_pins {
- cnss_default: cnss_default {
+ cnss_bootstrap_active: cnss_bootstrap_active {
mux {
pins = "gpio46";
function = "gpio";
@@ -1458,6 +1458,20 @@
config {
pins = "gpio46";
drive-strength = <16>;
+ output-high;
+ bias-pull-up;
+ };
+ };
+ cnss_bootstrap_sleep: cnss_bootstrap_sleep {
+ mux {
+ pins = "gpio46";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio46";
+ drive-strength = <2>;
+ output-low;
bias-pull-down;
};
};
@@ -2717,5 +2731,47 @@
};
};
};
+
+ pmx_pps: pmx_pps {
+ mux {
+ pins = "gpio22";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio22";
+ drive-strength = <16>; /* 16 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+
+ can_reset {
+ can_rst_on: rst_on {
+ mux {
+ pins = "gpio71";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio71";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-up;
+ };
+ };
+
+ can_rst_off: rst_off {
+ mux {
+ pins = "gpio71";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio71";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-up;
+ output-high;
+ };
+ };
+ };
};
};
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-sde.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
index 6d0e5c4cb920..b0688668e667 100644
--- a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
@@ -20,6 +20,8 @@
"vbif_phys",
"vbif_nrt_phys";
+ contiguous-region = <&cont_splash_mem &cont_splash_mem_hdmi>;
+
/* clock and supply entries */
clocks = <&clock_mmss clk_mdss_ahb_clk>,
<&clock_mmss clk_mdss_axi_clk>,
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index dcd884b4a745..505e325db1f5 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -176,6 +176,31 @@
soc: soc { };
+ firmware: firmware {
+ android {
+ compatible = "android,firmware";
+ fstab {
+ compatible = "android,fstab";
+ vendor {
+ compatible = "android,vendor";
+ dev = "/dev/block/platform/soc/7464900.sdhci/by-name/vendor";
+ type = "ext4";
+ mnt_flags = "ro,barrier=1,discard";
+ fsmgr_flags = "wait,verify";
+ status = "ok";
+ };
+ system {
+ compatible = "android,system";
+ dev = "/dev/block/platform/soc/7464900.sdhci/by-name/system";
+ type = "ext4";
+ mnt_flags = "ro,barrier=1,discard";
+ fsmgr_flags = "wait,verify";
+ status = "ok";
+ };
+ };
+ };
+ };
+
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
@@ -2335,15 +2360,17 @@
qcom,cnss {
compatible = "qcom,cnss";
wlan-bootstrap-gpio = <&tlmm 46 0>;
- wlan-en-gpio = <&pm8994_gpios 8 0>;
+ vdd-wlan-en-supply = <&wlan_en_vreg>;
vdd-wlan-supply = <&rome_vreg>;
vdd-wlan-io-supply = <&pm8994_s4>;
vdd-wlan-xtal-supply = <&pm8994_l30>;
vdd-wlan-core-supply = <&pm8994_s3>;
wlan-ant-switch-supply = <&pm8994_l18_pin_ctrl>;
+ qcom,wlan-en-vreg-support;
qcom,notify-modem-status;
- pinctrl-names = "default";
- pinctrl-0 = <&cnss_default>;
+ pinctrl-names = "bootstrap_active", "bootstrap_sleep";
+ pinctrl-0 = <&cnss_bootstrap_active>;
+ pinctrl-1 = <&cnss_bootstrap_sleep>;
qcom,wlan-rc-num = <0>;
qcom,wlan-ramdump-dynamic = <0x200000>;
@@ -3355,6 +3382,82 @@
};
};
+ qcom,msm-dai-tdm-pri-rx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37120>;
+ qcom,msm-cpudai-tdm-group-num-ports = <4>;
+ qcom,msm-cpudai-tdm-group-port-id = <36864 36866 36868 36870>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <1>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
+ dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36864>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36866>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36868>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36870>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
+ qcom,msm-dai-tdm-pri-tx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37121>;
+ qcom,msm-cpudai-tdm-group-num-ports = <4>;
+ qcom,msm-cpudai-tdm-group-port-id = <36865 36867 36869 36871>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <1>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
+ dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36865>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36867>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36869>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36871>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
qcom,msm-dai-tdm-sec-tx {
compatible = "qcom,msm-dai-tdm";
qcom,msm-cpudai-tdm-group-id = <37137>;
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
index 48d5cb78611b..f5c33063643d 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
+++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
@@ -42,6 +42,9 @@
i2c@75b6000 { /* BLSP8 */
/* ADV7533 HDMI Bridge Chip removed on ADP Lite */
+ adv7533@3d {
+ status = "disabled";
+ };
adv7533@39 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-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-camera-sensor-svr20.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-svr20.dtsi
new file mode 100644
index 000000000000..9d408ee5f3a7
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-svr20.dtsi
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ led_flash0: qcom,camera-flash@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>;
+ qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>;
+ qcom,switch-source = <&pmi8998_switch0>;
+ status = "ok";
+ };
+
+ led_flash1: qcom,camera-flash@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-source = <&pmi8998_flash2>;
+ qcom,torch-source = <&pmi8998_torch2>;
+ qcom,switch-source = <&pmi8998_switch1>;
+ status = "ok";
+ };
+};
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ gpios = <&tlmm 27 0>;
+ qcom,gpio-vaf = <0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_VAF";
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_actuator_vaf_active>;
+ pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ };
+
+ actuator1: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ gpios = <&tlmm 27 0>;
+ qcom,gpio-vaf = <0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_VAF";
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_actuator_vaf_active>;
+ pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ };
+
+ ois0: qcom,ois@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,ois";
+ qcom,cci-master = <0>;
+ gpios = <&tlmm 27 0>;
+ qcom,gpio-vaf = <0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_VAF";
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_actuator_vaf_active>;
+ pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ status = "disabled";
+ };
+
+ eeprom0: qcom,eeprom@0 {
+ cell-index = <0>;
+ reg = <0>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&pm8998_s3>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <0 3312000 1352000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1352000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active
+ &cam_actuator_vaf_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend
+ &cam_actuator_vaf_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 30 0>,
+ <&pm8998_gpios 20 0>,
+ <&tlmm 29 0>,
+ <&tlmm 27 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-vaf = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VDIG",
+ "CAM_VANA",
+ "CAM_VAF";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk0_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ eeprom1: qcom,eeprom@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,eeprom";
+ cam_vdig-supply = <&pm8998_lvs1>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <0 0 3312000>;
+ qcom,cam-vreg-max-voltage = <0 0 3600000>;
+ qcom,cam-vreg-op-mode = <0 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk2_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 15 0>,
+ <&tlmm 9 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VANA1";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk2_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ eeprom2: qcom,eeprom@2 {
+ cell-index = <2>;
+ reg = <0x2>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&pm8998_lvs1>;
+ /*cam_vana-supply = <&pm8998_l22>;*/
+ cam_vdig-supply = <&pm8998_s3>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage =
+ <0 2864000 1352000>;
+ qcom,cam-vreg-max-voltage =
+ <0 2864000 1352000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_front_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_front_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>,
+ <&pm8998_gpios 9 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_VDIG";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk1_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <270>;
+ cam_vio-supply = <&pm8998_l8>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&pm8998_l9>;
+ cam_v_custom1-supply = <&pm8998_lvs1>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vana",
+ "cam_vio", "cam_v_custom1";
+ qcom,cam-vreg-min-voltage = <1808000 3312000 1200000 0>;
+ qcom,cam-vreg-max-voltage = <2960000 3600000 1200000 0>;
+ qcom,cam-vreg-op-mode = <0 80000 105000 0>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_6dofl_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_6dofl_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 148 0>,
+ <&tlmm 93 0>,
+ <&tlmm 52 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VANA",
+ "CAM_VDIG";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk0_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom1>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8998_lvs1>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <0 0 3312000>;
+ qcom,cam-vreg-max-voltage = <0 0 3600000>;
+ qcom,cam-vreg-op-mode = <0 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk2_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 15 0>,
+ <&tlmm 9 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VANA1";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk2_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+ qcom,camera@2 {
+ cell-index = <2>;
+ compatible = "qcom,camera";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom2>;
+ qcom,led-flash-src = <&led_flash1>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&pm8998_s3>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage =
+ <0 3312000 1352000>;
+ qcom,cam-vreg-max-voltage =
+ <0 3600000 1352000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_front_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_front_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>,
+ <&pm8998_gpios 9 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_VDIG";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "disabled";
+ clocks = <&clock_mmss clk_mclk1_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+ qcom,camera@3 {
+ cell-index = <3>;
+ compatible = "qcom,camera";
+ reg = <0x3>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <270>;
+ cam_vio-supply = <&pm8998_l8>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&pm8998_l9>;
+ cam_v_custom1-supply = <&pm8998_lvs1>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vana",
+ "cam_vio", "cam_v_custom1";
+ qcom,cam-vreg-min-voltage = <1808000 3312000 1200000 0>;
+ qcom,cam-vreg-max-voltage = <2960000 3600000 1200000 0>;
+ qcom,cam-vreg-op-mode = <0 80000 105000 0>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_6dofr_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_6dofr_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 149 0>,
+ <&tlmm 93 0>,
+ <&tlmm 52 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET1",
+ "CAM_VANA1",
+ "CAM_VDIG1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk0_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+};
+
+&pm8998_gpios {
+ gpio@c800 { /* GPIO 9 - CAMERA SENSOR 2 VDIG */
+ qcom,mode = <1>; /* Output */
+ qcom,pull = <5>; /* No Pull */
+ qcom,vin-sel = <0>; /* VIN1 GPIO_LV */
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,invert = <0>; /* Invert */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "ok";
+ };
+
+ gpio@d300 { /* GPIO 20 - CAMERA SENSOR 0 VDIG */
+ qcom,mode = <1>; /* Output */
+ qcom,pull = <5>; /* No Pull */
+ qcom,vin-sel = <1>; /* VIN1 GPIO_MV */
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,invert = <0>; /* Invert */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "ok";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-coresight.dtsi b/arch/arm/boot/dts/qcom/msm8998-coresight.dtsi
index 4b81d2754255..24c91c6102a4 100644
--- a/arch/arm/boot/dts/qcom/msm8998-coresight.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-coresight.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1412,6 +1412,14 @@
<&tpda_spss_out_funnel_spss>;
};
};
+ port@2 {
+ reg = <1>;
+ funnel_spss_in_spss_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&spss_etm0_out_funnel_spss>;
+ };
+ };
};
};
@@ -1598,4 +1606,17 @@
};
};
};
+
+ dummy-spss-etm0 {
+ compatible = "qcom,coresight-dummy";
+
+ coresight-name = "coresight-spss-etm0";
+
+ port{
+ spss_etm0_out_funnel_spss: endpoint {
+ remote-endpoint =
+ <&funnel_spss_in_spss_etm0>;
+ };
+ };
+ };
};
diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
index 93b6a7664ed8..897ab12fe0a7 100644
--- a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
@@ -87,7 +87,6 @@
qcom,mdss-dsi-t-clk-post = <0x07>;
qcom,mdss-dsi-t-clk-pre = <0x25>;
qcom,mdss-dsi-tx-eot-append;
- qcom,cmd-sync-wait-broadcast;
qcom,esd-check-enabled;
qcom,mdss-dsi-min-refresh-rate = <55>;
qcom,mdss-dsi-max-refresh-rate = <60>;
@@ -107,7 +106,6 @@
qcom,mdss-dsi-t-clk-post = <0x0d>;
qcom,mdss-dsi-t-clk-pre = <0x2d>;
qcom,mdss-dsi-tx-eot-append;
- qcom,cmd-sync-wait-broadcast;
qcom,esd-check-enabled;
qcom,mdss-dsi-panel-status-check-mode = "reg_read";
qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
index 354ac830e0fa..f95ef2b84e2c 100644
--- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
@@ -14,7 +14,7 @@
sde_kms: qcom,sde_kms@c900000 {
compatible = "qcom,sde-kms";
reg = <0x0c900000 0x90000>,
- <0x0c9b0000 0x1040>;
+ <0x0c9b0000 0x2008>;
reg-names = "mdp_phys", "vbif_phys";
/* clock and supply entries */
@@ -52,23 +52,44 @@
/* hw blocks */
qcom,sde-off = <0x1000>;
+ qcom,sde-len = <0x458>;
+
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
0x2600 0x2800>;
+ qcom,sde-ctl-size = <0x94>;
+
qcom,sde-mixer-off = <0x45000 0x46000 0x47000
0x48000 0x49000 0x4a000>;
+ qcom,sde-mixer-size = <0x31c>;
+
qcom,sde-dspp-off = <0x55000 0x57000>;
+ qcom,sde-dspp-size = <0x17e0>;
+
qcom,sde-wb-off = <0x66000>;
+ qcom,sde-wb-size = <0x2dc>;
+
qcom,sde-wb-id = <2>;
qcom,sde-wb-xin-id = <6>;
qcom,sde-wb-clk-ctrl = <0x2bc 0x10>;
qcom,sde-intf-off = <0x6b000 0x6b800
0x6c000 0x6c800>;
+ qcom,sde-intf-size = <0x280>;
+
qcom,sde-intf-type = "dp", "dsi", "dsi", "hdmi";
+
qcom,sde-pp-off = <0x71000 0x71800
- 0x72000 0x72800>;
- qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0>;
+ 0x72000 0x72800 0x73000>;
+ qcom,sde-pp-slave = <0x0 0x0 0x0 0x0 0x1>;
+
+ qcom,sde-pp-size = <0xd4>;
+
+ qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0 0x0>;
qcom,sde-cdm-off = <0x7a200>;
- qcom,sde-dsc-off = <0x10000 0x10000 0x0 0x0>;
+ qcom,sde-cdm-size = <0x224>;
+
+ qcom,sde-dsc-off = <0x81000 0x81400>;
+ qcom,sde-dsc-size = <0x140>;
+
qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>;
qcom,sde-sspp-type = "vig", "vig", "vig", "vig",
@@ -78,6 +99,7 @@
qcom,sde-sspp-off = <0x5000 0x7000 0x9000 0xb000
0x25000 0x27000 0x29000 0x2b000
0x35000 0x37000>;
+ qcom,sde-sspp-src-size = <0x1ac>;
qcom,sde-sspp-xin-id = <0 4 8 12 1 5 9 13 2 10>;
@@ -113,6 +135,7 @@
qcom,sde-sspp-vig-blocks {
qcom,sde-vig-csc-off = <0x1a00>;
qcom,sde-vig-qseed-off = <0xa00>;
+ qcom,sde-vig-qseed-size = <0xa0>;
};
qcom,platform-supply-entries {
diff --git a/arch/arm/boot/dts/qcom/msm8998-svr20-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8998-svr20-pinctrl.dtsi
new file mode 100644
index 000000000000..1347fcef4251
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-svr20-pinctrl.dtsi
@@ -0,0 +1,3386 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ tlmm: pinctrl@03400000 {
+ compatible = "qcom,msm8998-pinctrl";
+ reg = <0x3400000 0xc00000>;
+ interrupts = <0 208 0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ uart_console_active: uart_console_active {
+ mux {
+ pins = "gpio4", "gpio5";
+ function = "blsp_uart8_a";
+ };
+
+ config {
+ pins = "gpio4", "gpio5";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ wcd9xxx_intr {
+ wcd_intr_default: wcd_intr_default{
+ mux {
+ pins = "gpio54";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio54";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ input-enable;
+ };
+ };
+ };
+
+ /* I2C CONFIGURATION */
+ i2c_1 {
+ i2c_1_active: i2c_1_active {
+ mux {
+ pins = "gpio2", "gpio3";
+ function = "blsp_i2c1";
+ };
+
+ config {
+ pins = "gpio2", "gpio3";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_1_sleep: i2c_1_sleep {
+ mux {
+ pins = "gpio2", "gpio3";
+ function = "blsp_i2c1";
+ };
+
+ config {
+ pins = "gpio2", "gpio3";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_2 {
+ i2c_2_active: i2c_2_active {
+ mux {
+ pins = "gpio32", "gpio33";
+ function = "blsp_i2c2";
+ };
+
+ config {
+ pins = "gpio32", "gpio33";
+ drive-strength = <8>;
+ bias-disable;
+ };
+ };
+
+ i2c_2_sleep: i2c_2_sleep {
+ mux {
+ pins = "gpio32", "gpio33";
+ function = "blsp_i2c2";
+ };
+
+ config {
+ pins = "gpio32", "gpio33";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_3 {
+ i2c_3_active: i2c_3_active {
+ mux {
+ pins = "gpio47", "gpio48";
+ function = "blsp_i2c3";
+ };
+
+ config {
+ pins = "gpio47", "gpio48";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_3_sleep: i2c_3_sleep {
+ mux {
+ pins = "gpio47", "gpio48";
+ function = "blsp_i2c3";
+ };
+
+ config {
+ pins = "gpio47", "gpio48";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_4 {
+ i2c_4_active: i2c_4_active {
+ mux {
+ pins = "gpio10", "gpio11";
+ function = "blsp_i2c4";
+ };
+
+ config {
+ pins = "gpio10", "gpio11";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_4_sleep: i2c_4_sleep {
+ mux {
+ pins = "gpio10", "gpio11";
+ function = "blsp_i2c4";
+ };
+
+ config {
+ pins = "gpio10", "gpio11";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_5 {
+ i2c_5_active: i2c_5_active {
+ mux {
+ pins = "gpio87", "gpio88";
+ function = "blsp_i2c5";
+ };
+
+ config {
+ pins = "gpio87", "gpio88";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_5_sleep: i2c_5_sleep {
+ mux {
+ pins = "gpio87", "gpio88";
+ function = "blsp_i2c5";
+ };
+
+ config {
+ pins = "gpio87", "gpio88";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+ };
+
+ i2c_6 {
+ i2c_6_active: i2c_6_active {
+ mux {
+ pins = "gpio43", "gpio44";
+ function = "blsp_i2c6";
+ };
+
+ config {
+ pins = "gpio43", "gpio44";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_6_sleep: i2c_6_sleep {
+ mux {
+ pins = "gpio43", "gpio44";
+ function = "blsp_i2c6";
+ };
+
+ config {
+ pins = "gpio43", "gpio44";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ nfc {
+ nfc_int_active: nfc_int_active {
+ /* active state */
+ mux {
+ /* GPIO 92 NFC Read Interrupt */
+ pins = "gpio92";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio92";
+ drive-strength = <6>; /* 6 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_int_suspend: nfc_int_suspend {
+ /* sleep state */
+ mux {
+ /* GPIO 92 NFC Read Interrupt */
+ pins = "gpio92";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio92";
+ drive-strength = <6>; /* 6 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_enable_active: nfc_enable_active {
+ /* active state */
+ mux {
+ /* 12: NFC ENABLE 116:ESE Enable */
+ pins = "gpio12", "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio12", "gpio116";
+ drive-strength = <6>; /* 6 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_enable_suspend: nfc_enable_suspend {
+ /* sleep state */
+ mux {
+ /* 12: NFC ENABLE 116:ESE Enable */
+ pins = "gpio12", "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio12", "gpio116";
+ drive-strength = <6>; /* 6 MA */
+ bias-disable;
+ };
+ };
+ };
+
+ i2c_7 {
+ i2c_7_active: i2c_7_active {
+ mux {
+ pins = "gpio55", "gpio56";
+ function = "blsp_i2c7";
+ };
+
+ config {
+ pins = "gpio55", "gpio56";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_7_sleep: i2c_7_sleep {
+ mux {
+ pins = "gpio55", "gpio56";
+ function = "blsp_i2c7";
+ };
+
+ config {
+ pins = "gpio55", "gpio56";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_8 {
+ i2c_8_active: i2c_8_active {
+ mux {
+ pins = "gpio6", "gpio7";
+ function = "blsp_i2c8";
+ };
+
+ config {
+ pins = "gpio6", "gpio7";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_8_sleep: i2c_8_sleep {
+ mux {
+ pins = "gpio6", "gpio7";
+ function = "blsp_i2c8";
+ };
+
+ config {
+ pins = "gpio6", "gpio7";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_9 {
+ i2c_9_active: i2c_9_active {
+ mux {
+ pins = "gpio51", "gpio52";
+ function = "blsp_i2c9";
+ };
+
+ config {
+ pins = "gpio51", "gpio52";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_9_sleep: i2c_9_sleep {
+ mux {
+ pins = "gpio51", "gpio52";
+ function = "blsp_i2c9";
+ };
+
+ config {
+ pins = "gpio51", "gpio52";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_10 {
+ i2c_10_active: i2c_10_active {
+ mux {
+ pins = "gpio67", "gpio68";
+ function = "blsp_i2c10";
+ };
+
+ config {
+ pins = "gpio67", "gpio68";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_10_sleep: i2c_10_sleep {
+ mux {
+ pins = "gpio67", "gpio68";
+ function = "blsp_i2c10";
+ };
+
+ config {
+ pins = "gpio67", "gpio68";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_11 {
+ i2c_11_active: i2c_11_active {
+ mux {
+ pins = "gpio60", "gpio61";
+ function = "blsp_i2c11";
+ };
+
+ config {
+ pins = "gpio60", "gpio61";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_11_sleep: i2c_11_sleep {
+ mux {
+ pins = "gpio60", "gpio61";
+ function = "blsp_i2c11";
+ };
+
+ config {
+ pins = "gpio60", "gpio61";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_12 {
+ i2c_12_active: i2c_12_active {
+ mux {
+ pins = "gpio83", "gpio84";
+ function = "blsp_i2c12";
+ };
+
+ config {
+ pins = "gpio83", "gpio84";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_12_sleep: i2c_12_sleep {
+ mux {
+ pins = "gpio83", "gpio84";
+ function = "blsp_i2c12";
+ };
+
+ config {
+ pins = "gpio83", "gpio84";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ /* SPI CONFIGURATION */
+
+ spi_1 {
+ spi_1_active: spi_1_active {
+ mux {
+ pins = "gpio0", "gpio1",
+ "gpio2", "gpio3";
+ function = "blsp_spi1";
+ };
+
+ config {
+ pins = "gpio0", "gpio1",
+ "gpio2", "gpio3";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_1_sleep: spi_1_sleep {
+ mux {
+ pins = "gpio0", "gpio1",
+ "gpio2", "gpio3";
+ function = "blsp_spi1";
+ };
+
+ config {
+ pins = "gpio0", "gpio1",
+ "gpio2", "gpio3";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_2 {
+ spi_2_active: spi_2_active {
+ mux {
+ pins = "gpio31", "gpio34",
+ "gpio32", "gpio33";
+ function = "blsp_spi2";
+ };
+
+ config {
+ pins = "gpio31", "gpio34",
+ "gpio32", "gpio33";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_2_sleep: spi_2_sleep {
+ mux {
+ pins = "gpio31", "gpio34",
+ "gpio32", "gpio33";
+ function = "blsp_spi2";
+ };
+
+ config {
+ pins = "gpio31", "gpio34",
+ "gpio32", "gpio33";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_3 {
+ spi_3_active: spi_3_active {
+ mux {
+ pins = "gpio45", "gpio46",
+ "gpio47", "gpio48";
+ function = "blsp_spi3";
+ };
+
+ config {
+ pins = "gpio45", "gpio46",
+ "gpio47", "gpio48";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_3_sleep: spi_3_sleep {
+ mux {
+ pins = "gpio45", "gpio46",
+ "gpio47", "gpio48";
+ function = "blsp_spi3";
+ };
+
+ config {
+ pins = "gpio45", "gpio46",
+ "gpio47", "gpio48";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ pcie0 {
+ pcie0_clkreq_default: pcie0_clkreq_default {
+ mux {
+ pins = "gpio36";
+ function = "pci_e0";
+ };
+
+ config {
+ pins = "gpio36";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ pcie0_perst_default: pcie0_perst_default {
+ mux {
+ pins = "gpio35";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio35";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ pcie0_wake_default: pcie0_wake_default {
+ mux {
+ pins = "gpio37";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio37";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ hph_en0_ctrl {
+ hph_en0_idle: hph_en0_idle {
+ mux {
+ pins = "gpio67";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio67";
+ drive-strength = <2>;
+ bias-pull-down;
+ output-low;
+ };
+ };
+ hph_en0_active: hph_en0_active {
+ mux {
+ pins = "gpio67";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio67";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ hph_en1_ctrl {
+ hph_en1_idle: hph_en1_idle {
+ mux {
+ pins = "gpio68";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio68";
+ drive-strength = <2>;
+ bias-pull-down;
+ output-low;
+ };
+ };
+ hph_en1_active: hph_en1_active {
+ mux {
+ pins = "gpio68";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio68";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ wcd_gnd_mic_swap {
+ wcd_gnd_mic_swap_idle: wcd_gnd_mic_swap_idle {
+ mux {
+ pins = "gpio75";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio75";
+ drive-strength = <2>;
+ bias-pull-down;
+ output-low;
+ };
+ };
+ wcd_gnd_mic_swap_active: wcd_gnd_mic_swap_active {
+ mux {
+ pins = "gpio75";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio75";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ /* USB C analog configuration */
+ wcd_usbc_analog_en1 {
+ wcd_usbc_analog_en1_idle: wcd_usbc_ana_en1_idle {
+ mux {
+ pins = "gpio59";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio59";
+ drive-strength = <2>;
+ bias-pull-down;
+ output-low;
+ };
+ };
+
+ wcd_usbc_analog_en1_active: wcd_usbc_ana_en1_active {
+ mux {
+ pins = "gpio59";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio59";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ wcd_usbc_analog_en2n {
+ wcd_usbc_analog_en2n_idle: wcd_usbc_ana_en2n_idle {
+ mux {
+ pins = "gpio60";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio60";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+
+ wcd_usbc_analog_en2n_active: wcd_usbc_ana_en2n_active {
+ mux {
+ pins = "gpio60";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio60";
+ drive-strength = <2>;
+ bias-pull-down;
+ output-low;
+ };
+ };
+ };
+
+ cdc_reset_ctrl {
+ cdc_reset_sleep: cdc_reset_sleep {
+ mux {
+ pins = "gpio116";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio116";
+ drive-strength = <16>;
+ bias-disable;
+ output-low;
+ };
+ };
+ cdc_reset_active:cdc_reset_active {
+ mux {
+ pins = "gpio116";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio116";
+ drive-strength = <16>;
+ bias-pull-down;
+ output-high;
+ };
+ };
+ };
+
+ spi_4 {
+ spi_4_active: spi_4_active {
+ mux {
+ pins = "gpio8", "gpio9",
+ "gpio10", "gpio11";
+ function = "blsp_spi4";
+ };
+
+ config {
+ pins = "gpio8", "gpio9",
+ "gpio10", "gpio11";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_4_sleep: spi_4_sleep {
+ mux {
+ pins = "gpio8", "gpio9",
+ "gpio10", "gpio11";
+ function = "blsp_spi4";
+ };
+
+ config {
+ pins = "gpio8", "gpio9",
+ "gpio10", "gpio11";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spkr_1_sd_n {
+ spkr_1_sd_n_sleep: spkr_1_sd_n_sleep {
+ mux {
+ pins = "gpio111";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio111";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ input-enable;
+ };
+ };
+ spkr_1_sd_n_active: spkr_1_sd_n_active {
+ mux {
+ pins = "gpio111";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio111";
+ drive-strength = <16>; /* 16 mA */
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ spi_5 {
+ spi_5_active: spi_5_active {
+ mux {
+ pins = "gpio85", "gpio86",
+ "gpio87", "gpio88";
+ function = "blsp_spi5";
+ };
+
+ config {
+ pins = "gpio85", "gpio86",
+ "gpio87", "gpio88";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_5_sleep: spi_5_sleep {
+ mux {
+ pins = "gpio85", "gpio86",
+ "gpio87", "gpio88";
+ function = "blsp_spi5";
+ };
+
+ config {
+ pins = "gpio85", "gpio86",
+ "gpio87", "gpio88";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spkr_2_sd_n {
+ spkr_2_sd_n_sleep: spkr_2_sd_n_sleep {
+ mux {
+ pins = "gpio112";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio112";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ input-enable;
+ };
+ };
+ spkr_2_sd_n_active: spkr_2_sd_n_active {
+ mux {
+ pins = "gpio112";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio112";
+ drive-strength = <16>; /* 16 mA */
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ cci0_active: cci0_active {
+ mux {
+ /* CLK, DATA */
+ pins = "gpio17","gpio18"; // Only 2
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio17","gpio18";
+ bias-pull-up; /* PULL UP*/
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cci0_suspend: cci0_suspend {
+ mux {
+ /* CLK, DATA */
+ pins = "gpio17","gpio18";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio17","gpio18";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cci1_active: cci1_active {
+ mux {
+ /* CLK, DATA */
+ pins = "gpio19","gpio20";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio19","gpio20";
+ bias-pull-up; /* PULL UP*/
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cci1_suspend: cci1_suspend {
+ mux {
+ /* CLK, DATA */
+ pins = "gpio19","gpio20";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio19","gpio20";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_actuator_vaf_active: cam_actuator_vaf_active {
+ /* ACTUATOR POWER */
+ mux {
+ pins = "gpio27";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio27";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_actuator_vaf_suspend: cam_actuator_vaf_suspend {
+ /* ACTUATOR POWER */
+ mux {
+ pins = "gpio27";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio27";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk0_active: cam_sensor_mclk0_active {
+ /* MCLK0 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio13";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio13";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk0_suspend: cam_sensor_mclk0_suspend {
+ /* MCLK0 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio13";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio13";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_6dofl_active: cam_sensor_6dofl_active {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio148","gpio29";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio148","gpio29";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_6dofr_active: cam_sensor_6dofr_active {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio149","gpio29";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio149","gpio29";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_6dofl_suspend: cam_sensor_6dofl_suspend {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio148","gpio29";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio148","gpio29";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_6dofr_suspend: cam_sensor_6dofr_suspend {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio149","gpio29";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio149","gpio29";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_rear_active: cam_sensor_rear_active {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio30","gpio29";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio30","gpio29";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+ max_volt_active: max_volt_active {
+ /* RESET */
+ mux {
+ pins = "gpio128", "gpio129",
+ "gpio130", "gpio133";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio128", "gpio129",
+ "gpio130", "gpio133";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+ max_volt_suspend: max_volt_suspend {
+ /* RESET */
+ mux {
+ pins = "gpio128", "gpio129",
+ "gpio130", "gpio133";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio128", "gpio129",
+ "gpio130", "gpio133";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+ max_rst_active: max_rst_active {
+ /* RESET */
+ mux {
+ pins = "gpio30";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio30";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+ max_rst_suspend: max_rst_suspend {
+ /* RESET */
+ mux {
+ pins = "gpio30";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio30";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+
+ spi_6 {
+ spi_6_active: spi_6_active {
+ mux {
+ pins = "gpio41", "gpio42",
+ "gpio43", "gpio44";
+ function = "blsp_spi6";
+ };
+
+ config {
+ pins = "gpio41", "gpio42",
+ "gpio43", "gpio44";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_6_sleep: spi_6_sleep {
+ mux {
+ pins = "gpio41", "gpio42",
+ "gpio43", "gpio44";
+ function = "blsp_spi6";
+ };
+
+ config {
+ pins = "gpio41", "gpio42",
+ "gpio43", "gpio44";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_7 {
+ spi_7_active: spi_7_active {
+ mux {
+ pins = "gpio53", "gpio54",
+ "gpio55", "gpio56";
+ function = "blsp_spi7";
+ };
+
+ config {
+ pins = "gpio53", "gpio54",
+ "gpio55", "gpio56";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_7_sleep: spi_7_sleep {
+ mux {
+ pins = "gpio53", "gpio54",
+ "gpio55", "gpio56";
+ function = "blsp_spi7";
+ };
+
+ config {
+ pins = "gpio53", "gpio54",
+ "gpio55", "gpio56";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_8 {
+ spi_8_active: spi_8_active {
+ mux {
+ pins = "gpio4", "gpio5",
+ "gpio6", "gpio7";
+ function = "blsp_spi8";
+ };
+
+ config {
+ pins = "gpio4", "gpio5",
+ "gpio6", "gpio7";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_8_sleep: spi_8_sleep {
+ mux {
+ pins = "gpio4", "gpio5",
+ "gpio6", "gpio7";
+ function = "blsp_spi8";
+ };
+
+ config {
+ pins = "gpio4", "gpio5",
+ "gpio6", "gpio7";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_9 {
+ spi_9_active: spi_9_active {
+ mux {
+ pins = "gpio49", "gpio50",
+ "gpio51", "gpio52";
+ function = "blsp_spi9";
+ };
+
+ config {
+ pins = "gpio49", "gpio50",
+ "gpio51", "gpio52";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_9_sleep: spi_9_sleep {
+ mux {
+ pins = "gpio49", "gpio50",
+ "gpio51", "gpio52";
+ function = "blsp_spi9";
+ };
+
+ config {
+ pins = "gpio49", "gpio50",
+ "gpio51", "gpio52";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_10 {
+ spi_10_active: spi_10_active {
+ mux {
+ pins = "gpio65", "gpio66",
+ "gpio67", "gpio68";
+ function = "blsp_spi10";
+ };
+
+ config {
+ pins = "gpio65", "gpio66",
+ "gpio67", "gpio68";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_10_sleep: spi_10_sleep {
+ mux {
+ pins = "gpio65", "gpio66",
+ "gpio67", "gpio68";
+ function = "blsp_spi10";
+ };
+
+ config {
+ pins = "gpio65", "gpio66",
+ "gpio67", "gpio68";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_11 {
+ spi_11_active: spi_11_active {
+ mux {
+ pins = "gpio58", "gpio59",
+ "gpio60", "gpio61";
+ function = "blsp_spi11";
+ };
+
+ config {
+ pins = "gpio58", "gpio59",
+ "gpio60", "gpio61";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_11_sleep: spi_11_sleep {
+ mux {
+ pins = "gpio58", "gpio59",
+ "gpio60", "gpio61";
+ function = "blsp_spi11";
+ };
+
+ config {
+ pins = "gpio58", "gpio59",
+ "gpio60", "gpio61";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_12 {
+ spi_12_active: spi_12_active {
+ mux {
+ pins = "gpio81", "gpio82",
+ "gpio83", "gpio84";
+ function = "blsp_spi12";
+ };
+
+ config {
+ pins = "gpio81", "gpio82",
+ "gpio83", "gpio84";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_12_sleep: spi_12_sleep {
+ mux {
+ pins = "gpio81", "gpio82",
+ "gpio83", "gpio84";
+ function = "blsp_spi12";
+ };
+
+ config {
+ pins = "gpio81", "gpio82",
+ "gpio83", "gpio84";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ /* HS UART CONFIGURATION */
+ blsp1_uart1_active: blsp1_uart1_active {
+ mux {
+ pins = "gpio0", "gpio1", "gpio2", "gpio3";
+ function = "blsp_uart1_a";
+ };
+
+ config {
+ pins = "gpio0", "gpio1", "gpio2", "gpio3";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart1_sleep: blsp1_uart1_sleep {
+ mux {
+ pins = "gpio0", "gpio1", "gpio2", "gpio3";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio0", "gpio1", "gpio2", "gpio3";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ cam_sensor_rear_suspend: cam_sensor_rear_suspend {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio30","gpio29";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio30","gpio29";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk1_active: cam_sensor_mclk1_active {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio14";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio14";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk1_suspend: cam_sensor_mclk1_suspend {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio14";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio14";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_rear2_active: cam_sensor_rear2_active {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio9","gpio8";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio9","gpio8";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ blsp1_uart2_active: blsp1_uart2_active {
+ mux {
+ pins = "gpio31", "gpio34", "gpio33", "gpio32";
+ function = "blsp_uart2_a";
+ };
+
+ config {
+ pins = "gpio31", "gpio34", "gpio33", "gpio32";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart2_sleep: blsp1_uart2_sleep {
+ mux {
+ pins = "gpio31", "gpio34", "gpio33", "gpio32";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio31", "gpio34", "gpio33", "gpio32";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart3: blsp1_uart3 {
+ blsp1_uart3_tx_active: blsp1_uart3_tx_active {
+ mux {
+ pins = "gpio45";
+ function = "blsp_uart3_a";
+ };
+
+ config {
+ pins = "gpio45";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart3_tx_sleep: blsp1_uart3_tx_sleep {
+ mux {
+ pins = "gpio45";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio45";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ blsp1_uart3_rxcts_active: blsp1_uart3_rxcts_active {
+ mux {
+ pins = "gpio46", "gpio47";
+ function = "blsp_uart3_a";
+ };
+
+ config {
+ pins = "gpio46", "gpio47";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart3_rxcts_sleep: blsp1_uart3_rxcts_sleep {
+ mux {
+ pins = "gpio46", "gpio47";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio46", "gpio47";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+
+ blsp1_uart3_rfr_active: blsp1_uart3_rfr_active {
+ mux {
+ pins = "gpio48";
+ function = "blsp_uart3_a";
+ };
+
+ config {
+ pins = "gpio48";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart3_rfr_sleep: blsp1_uart3_rfr_sleep {
+ mux {
+ pins = "gpio48";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio48";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+ };
+
+ cam_sensor_rear2_suspend: cam_sensor_rear2_suspend {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio9","gpio8";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio9","gpio8";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk2_active: cam_sensor_mclk2_active {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio15";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio15";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk2_suspend: cam_sensor_mclk2_suspend {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio15";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio15";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk3_active: cam_sensor_mclk3_active {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio16";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio16";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk3_suspend: cam_sensor_mclk3_suspend {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio16";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio16";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
+
+
+ cam_sensor_front_active: cam_sensor_front_active {
+ /* RESET VANA*/
+ mux {
+ pins = "gpio28", "gpio29";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio28", "gpio29";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ blsp2_uart1_active: blsp2_uart1_active {
+ mux {
+ pins = "gpio53", "gpio54", "gpio55", "gpio56";
+ function = "blsp_uart7_a";
+ };
+
+ config {
+ pins = "gpio53", "gpio54", "gpio55", "gpio56";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp2_uart1_sleep: blsp2_uart1_sleep {
+ mux {
+ pins = "gpio53", "gpio54", "gpio55", "gpio56";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio53", "gpio54", "gpio55", "gpio56";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp2_uart2_active: blsp2_uart2_active {
+ mux {
+ pins = "gpio4", "gpio5", "gpio6", "gpio7";
+ function = "blsp_uart8_a";
+ };
+
+ config {
+ pins = "gpio4", "gpio5", "gpio6", "gpio7";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp2_uart2_sleep: blsp2_uart2_sleep {
+ mux {
+ pins = "gpio4", "gpio5", "gpio6", "gpio7";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio4", "gpio5", "gpio6", "gpio7";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ cam_sensor_front_suspend: cam_sensor_front_suspend {
+ /* RESET */
+ mux {
+ pins = "gpio28";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio28";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ pmx_mdss: pmx_mdss {
+ mdss_dsi_active: mdss_dsi_active {
+ mux {
+ pins = "gpio94", "gpio97", "gpio51";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio94", "gpio97", "gpio51";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable = <0>; /* no pull */
+ };
+ };
+ mdss_dsi_suspend: mdss_dsi_suspend {
+ mux {
+ pins = "gpio94", "gpio97", "gpio51";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio94", "gpio97", "gpio51";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+ };
+
+ pmx_mdss_te {
+ mdss_te_active: mdss_te_active {
+ mux {
+ pins = "gpio97";
+ function = "mdp_vsync_b";
+ };
+ config {
+ pins = "gpio97";
+ drive-strength = <2>; /* 8 mA */
+ bias-pull-down; /* pull down*/
+ };
+ };
+
+ mdss_te_suspend: mdss_te_suspend {
+ mux {
+ pins = "gpio97";
+ function = "mdp_vsync_b";
+ };
+ config {
+ pins = "gpio97";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+ };
+
+ mdss_dp_aux_active: mdss_dp_aux_active {
+ mux {
+ pins = "gpio77", "gpio78";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio77", "gpio78";
+ bias-disable = <0>; /* no pull */
+ drive-strength = <8>;
+ };
+ };
+
+ mdss_dp_aux_suspend: mdss_dp_aux_suspend {
+ mux {
+ pins = "gpio77", "gpio78";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio77", "gpio78";
+ bias-pull-down;
+ drive-strength = <2>;
+ };
+ };
+
+ mdss_dp_usbplug_cc_active: mdss_dp_usbplug_cc_active {
+ mux {
+ pins = "gpio38";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio38";
+ bias-disable;
+ drive-strength = <16>;
+ };
+ };
+
+ mdss_dp_usbplug_cc_suspend: mdss_dp_usbplug_cc_suspend {
+ mux {
+ pins = "gpio38";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio38";
+ bias-pull-down;
+ drive-strength = <2>;
+ };
+ };
+
+ mdss_dp_hpd_active: mdss_dp_hpd_active {
+ mux {
+ pins = "gpio34";
+ function = "edp_hot";
+ };
+
+ config {
+ pins = "gpio34";
+ bias-pull-down;
+ drive-strength = <16>;
+ };
+ };
+
+ mdss_dp_hpd_suspend: mdss_dp_hpd_suspend {
+ mux {
+ pins = "gpio34";
+ function = "edp_hot";
+ };
+
+ config {
+ pins = "gpio34";
+ bias-pull-down;
+ drive-strength = <2>;
+ };
+ };
+
+ blsp2_uart3_active: blsp2_uart3_active {
+ mux {
+ pins = "gpio49", "gpio50", "gpio51", "gpio52";
+ function = "blsp_uart9_a";
+ };
+
+ config {
+ pins = "gpio49", "gpio50", "gpio51", "gpio52";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp2_uart3_sleep: blsp2_uart3_sleep {
+ mux {
+ pins = "gpio49", "gpio50", "gpio51", "gpio52";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio49", "gpio50", "gpio51", "gpio52";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ /* add pingrp for touchscreen */
+ pmx_ts_int_active {
+ ts_int_active: ts_int_active {
+ mux {
+ pins = "gpio125";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio125";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ pmx_ts_int_suspend {
+ ts_int_suspend1: ts_int_suspend1 {
+ mux {
+ pins = "gpio125";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio125";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ pmx_ts_reset_active {
+ ts_reset_active: ts_reset_active {
+ mux {
+ pins = "gpio89";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio89";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ pmx_ts_reset_suspend {
+ ts_reset_suspend1: ts_reset_suspend1 {
+ mux {
+ pins = "gpio89";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio89";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ pmx_ts_release {
+ ts_release: ts_release {
+ mux {
+ pins = "gpio125", "gpio89";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio125", "gpio89";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ ts_mux {
+ ts_active: ts_active {
+ mux {
+ pins = "gpio89", "gpio125";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio89", "gpio125";
+ drive-strength = <16>;
+ bias-pull-up;
+ };
+ };
+
+ ts_reset_suspend: ts_reset_suspend {
+ mux {
+ pins = "gpio89";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio89";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ ts_int_suspend: ts_int_suspend {
+ mux {
+ pins = "gpio125";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio125";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+ };
+
+ ufs_dev_reset_assert: ufs_dev_reset_assert {
+ config {
+ pins = "ufs_reset";
+ bias-pull-down; /* default: pull down */
+ /*
+ * UFS_RESET driver strengths are having
+ * different values/steps compared to typical
+ * GPIO drive strengths.
+ *
+ * Following table clarifies:
+ *
+ * HDRV value | UFS_RESET | Typical GPIO
+ * (dec) | (mA) | (mA)
+ * 0 | 0.8 | 2
+ * 1 | 1.55 | 4
+ * 2 | 2.35 | 6
+ * 3 | 3.1 | 8
+ * 4 | 3.9 | 10
+ * 5 | 4.65 | 12
+ * 6 | 5.4 | 14
+ * 7 | 6.15 | 16
+ *
+ * POR value for UFS_RESET HDRV is 3 which means
+ * 3.1mA and we want to use that. Hence just
+ * specify 8mA to "drive-strength" binding and
+ * that should result into writing 3 to HDRV
+ * field.
+ */
+ drive-strength = <8>; /* default: 3.1 mA */
+ output-low; /* active low reset */
+ };
+ };
+
+ ufs_dev_reset_deassert: ufs_dev_reset_deassert {
+ config {
+ pins = "ufs_reset";
+ bias-pull-down; /* default: pull down */
+ /*
+ * default: 3.1 mA
+ * check comments under ufs_dev_reset_assert
+ */
+ drive-strength = <8>;
+ output-high; /* active low reset */
+ };
+ };
+
+ sdc2_clk_on: sdc2_clk_on {
+ config {
+ pins = "sdc2_clk";
+ bias-disable; /* NO pull */
+ drive-strength = <16>; /* 16 MA */
+ };
+ };
+
+ sdc2_clk_off: sdc2_clk_off {
+ config {
+ pins = "sdc2_clk";
+ bias-disable; /* NO pull */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc2_cmd_on: sdc2_cmd_on {
+ config {
+ pins = "sdc2_cmd";
+ bias-pull-up; /* pull up */
+ drive-strength = <10>; /* 10 MA */
+ };
+ };
+
+ sdc2_cmd_off: sdc2_cmd_off {
+ config {
+ pins = "sdc2_cmd";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc2_data_on: sdc2_data_on {
+ config {
+ pins = "sdc2_data";
+ bias-pull-up; /* pull up */
+ drive-strength = <10>; /* 10 MA */
+ };
+ };
+
+ sdc2_data_off: sdc2_data_off {
+ config {
+ pins = "sdc2_data";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc2_cd_on: sdc2_cd_on {
+ mux {
+ pins = "gpio95";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio95";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc2_cd_off: sdc2_cd_off {
+ mux {
+ pins = "gpio95";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio95";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+
+ };
+
+ led_enable: led_enable {
+ mux {
+ pins = "gpio21";
+ drive_strength = <16>;
+ output-high;
+ };
+ };
+
+ led_disable: led_disable {
+ mux {
+ pins = "gpio21";
+ drive_strength = <2>;
+ output-low;
+ };
+ };
+
+ trigout_a: trigout_a {
+ mux {
+ pins = "gpio58";
+ function = "qdss_cti1_a";
+ };
+
+ config {
+ pins = "gpio58";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ mdss_hdmi_5v_active: mdss_hdmi_5v_active {
+ mux {
+ pins = "gpio133";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio133";
+ bias-pull-up;
+ drive-strength = <16>;
+ };
+ };
+
+ mdss_hdmi_5v_suspend: mdss_hdmi_5v_suspend {
+ mux {
+ pins = "gpio133";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio133";
+ bias-pull-down;
+ drive-strength = <2>;
+ };
+ };
+
+
+ mdss_hdmi_hpd_active: mdss_hdmi_hpd_active {
+ mux {
+ pins = "gpio34";
+ function = "hdmi_hot";
+ };
+
+ config {
+ pins = "gpio34";
+ bias-pull-down;
+ drive-strength = <16>;
+ };
+ };
+
+ mdss_hdmi_hpd_suspend: mdss_hdmi_hpd_suspend {
+ mux {
+ pins = "gpio34";
+ function = "hdmi_hot";
+ };
+
+ config {
+ pins = "gpio34";
+ bias-pull-down;
+ drive-strength = <2>;
+ };
+ };
+
+ mdss_hdmi_ddc_active: mdss_hdmi_ddc_active {
+ mux {
+ pins = "gpio32", "gpio33";
+ function = "hdmi_ddc";
+ };
+
+ config {
+ pins = "gpio32", "gpio33";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ mdss_hdmi_ddc_suspend: mdss_hdmi_ddc_suspend {
+ mux {
+ pins = "gpio32", "gpio33";
+ function = "hdmi_ddc";
+ };
+
+ config {
+ pins = "gpio32", "gpio33";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ mdss_hdmi_cec_active: mdss_hdmi_cec_active {
+ mux {
+ pins = "gpio31";
+ function = "hdmi_cec";
+ };
+
+ config {
+ pins = "gpio31";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ mdss_hdmi_cec_suspend: mdss_hdmi_cec_suspend {
+ mux {
+ pins = "gpio31";
+ function = "hdmi_cec";
+ };
+
+ config {
+ pins = "gpio31";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ tsif0_signals_active: tsif0_signals_active {
+ tsif1_clk {
+ pins = "gpio89"; /* TSIF0 CLK */
+ function = "tsif1_clk";
+ };
+ tsif1_en {
+ pins = "gpio90"; /* TSIF0 Enable */
+ function = "tsif1_en";
+ };
+ tsif1_data {
+ pins = "gpio91"; /* TSIF0 DATA */
+ function = "tsif1_data";
+ };
+ signals_cfg {
+ pins = "gpio89", "gpio90", "gpio91";
+ drive_strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+
+ /* sync signal is only used if configured to mode-2 */
+ tsif0_sync_active: tsif0_sync_active {
+ tsif1_sync {
+ pins = "gpio9"; /* TSIF0 SYNC */
+ function = "tsif1_sync";
+ drive_strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+
+ tsif1_signals_active: tsif1_signals_active {
+ tsif2_clk {
+ pins = "gpio93"; /* TSIF1 CLK */
+ function = "tsif2_clk";
+ };
+ tsif2_en {
+ pins = "gpio94"; /* TSIF1 Enable */
+ function = "tsif2_en";
+ };
+ tsif2_data {
+ pins = "gpio95"; /* TSIF1 DATA */
+ function = "tsif2_data";
+ };
+ signals_cfg {
+ pins = "gpio93", "gpio94", "gpio95";
+ drive_strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+
+ /* sync signal is only used if configured to mode-2 */
+ tsif1_sync_active: tsif1_sync_active {
+ tsif2_sync {
+ pins = "gpio96"; /* TSIF1 SYNC */
+ function = "tsif2_sync";
+ drive_strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+
+ pri_aux_pcm_clk {
+ pri_aux_pcm_clk_sleep: pri_aux_pcm_clk_sleep {
+ mux {
+ pins = "gpio65";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_aux_pcm_clk_active: pri_aux_pcm_clk_active {
+ mux {
+ pins = "gpio65";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_aux_pcm_sync {
+ pri_aux_pcm_sync_sleep: pri_aux_pcm_sync_sleep {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_aux_pcm_sync_active: pri_aux_pcm_sync_active {
+ mux {
+ pins = "gpio66";
+ function = "pri_mi2s_ws";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_aux_pcm_din {
+ pri_aux_pcm_din_sleep: pri_aux_pcm_din_sleep {
+ mux {
+ pins = "gpio67";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio67";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_aux_pcm_din_active: pri_aux_pcm_din_active {
+ mux {
+ pins = "gpio67";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio67";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ pri_aux_pcm_dout {
+ pri_aux_pcm_dout_sleep: pri_aux_pcm_dout_sleep {
+ mux {
+ pins = "gpio68";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio68";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_aux_pcm_dout_active: pri_aux_pcm_dout_active {
+ mux {
+ pins = "gpio68";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio68";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_aux_pcm {
+ sec_aux_pcm_sleep: sec_aux_pcm_sleep {
+ mux {
+ pins = "gpio80", "gpio81";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio80", "gpio81";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_aux_pcm_active: sec_aux_pcm_active {
+ mux {
+ pins = "gpio80", "gpio81";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio80", "gpio81";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_aux_pcm_din {
+ sec_aux_pcm_din_sleep: sec_aux_pcm_din_sleep {
+ mux {
+ pins = "gpio82";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio82";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_aux_pcm_din_active: sec_aux_pcm_din_active {
+ mux {
+ pins = "gpio82";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio82";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_aux_pcm_dout {
+ sec_aux_pcm_dout_sleep: sec_aux_pcm_dout_sleep {
+ mux {
+ pins = "gpio83";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio83";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_aux_pcm_dout_active: sec_aux_pcm_dout_active {
+ mux {
+ pins = "gpio83";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio83";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_aux_pcm {
+ tert_aux_pcm_sleep: tert_aux_pcm_sleep {
+ mux {
+ pins = "gpio75", "gpio76";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio75", "gpio76";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_aux_pcm_active: tert_aux_pcm_active {
+ mux {
+ pins = "gpio75", "gpio76";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio75", "gpio76";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ tert_aux_pcm_din {
+ tert_aux_pcm_din_sleep: tert_aux_pcm_din_sleep {
+ mux {
+ pins = "gpio77";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio77";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_aux_pcm_din_active: tert_aux_pcm_din_active {
+ mux {
+ pins = "gpio77";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio77";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_aux_pcm_dout {
+ tert_aux_pcm_dout_sleep: tert_aux_pcm_dout_sleep {
+ mux {
+ pins = "gpio78";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio78";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_aux_pcm_dout_active: tert_aux_pcm_dout_active {
+ mux {
+ pins = "gpio78";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio78";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_aux_pcm {
+ quat_aux_pcm_sleep: quat_aux_pcm_sleep {
+ mux {
+ pins = "gpio58", "gpio59";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio58", "gpio59";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_aux_pcm_active: quat_aux_pcm_active {
+ mux {
+ pins = "gpio58", "gpio59";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio58", "gpio59";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ quat_aux_pcm_din {
+ quat_aux_pcm_din_sleep: quat_aux_pcm_din_sleep {
+ mux {
+ pins = "gpio60";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio60";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_aux_pcm_din_active: quat_aux_pcm_din_active {
+ mux {
+ pins = "gpio60";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio60";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_aux_pcm_dout {
+ quat_aux_pcm_dout_sleep: quat_aux_pcm_dout_sleep {
+ mux {
+ pins = "gpio61";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio61";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_aux_pcm_dout_active: quat_aux_pcm_dout_active {
+ mux {
+ pins = "gpio61";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio61";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ pri_mi2s_mclk {
+ pri_mi2s_mclk_sleep: pri_mi2s_mclk_sleep {
+ mux {
+ pins = "gpio64";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio64";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_mclk_active: pri_mi2s_mclk_active {
+ mux {
+ pins = "gpio64";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio64";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_mi2s_sck {
+ pri_mi2s_sck_sleep: pri_mi2s_sck_sleep {
+ mux {
+ pins = "gpio65";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_sck_active: pri_mi2s_sck_active {
+ mux {
+ pins = "gpio65";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_mi2s_ws {
+ pri_mi2s_ws_sleep: pri_mi2s_ws_sleep {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_ws_active: pri_mi2s_ws_active {
+ mux {
+ pins = "gpio66";
+ function = "pri_mi2s_ws";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_mi2s_sd0 {
+ pri_mi2s_sd0_sleep: pri_mi2s_sd0_sleep {
+ mux {
+ pins = "gpio67";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio67";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_sd0_active: pri_mi2s_sd0_active {
+ mux {
+ pins = "gpio67";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio67";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ pri_mi2s_sd1 {
+ pri_mi2s_sd1_sleep: pri_mi2s_sd1_sleep {
+ mux {
+ pins = "gpio68";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio68";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_sd1_active: pri_mi2s_sd1_active {
+ mux {
+ pins = "gpio68";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio68";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_mi2s_mclk {
+ sec_mi2s_mclk_sleep: sec_mi2s_mclk_sleep {
+ mux {
+ pins = "gpio79";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio79";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_mi2s_mclk_active: sec_mi2s_mclk_active {
+ mux {
+ pins = "gpio79";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio79";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_mi2s {
+ sec_mi2s_sleep: sec_mi2s_sleep {
+ mux {
+ pins = "gpio80", "gpio81";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio80", "gpio81";
+ drive-strength = <2>; /* 2 mA */
+ bias-disable; /* NO PULL */
+ input-enable;
+ };
+ };
+
+ sec_mi2s_active: sec_mi2s_active {
+ mux {
+ pins = "gpio80", "gpio81";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio80", "gpio81";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_mi2s_sd0 {
+ sec_mi2s_sd0_sleep: sec_mi2s_sd0_sleep {
+ mux {
+ pins = "gpio82";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio82";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_mi2s_sd0_active: sec_mi2s_sd0_active {
+ mux {
+ pins = "gpio82";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio82";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_mi2s_sd1 {
+ sec_mi2s_sd1_sleep: sec_mi2s_sd1_sleep {
+ mux {
+ pins = "gpio83";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio83";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_mi2s_sd1_active: sec_mi2s_sd1_active {
+ mux {
+ pins = "gpio83";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio83";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_mi2s_mclk {
+ tert_mi2s_mclk_sleep: tert_mi2s_mclk_sleep {
+ mux {
+ pins = "gpio74";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio74";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_mi2s_mclk_active: tert_mi2s_mclk_active {
+ mux {
+ pins = "gpio74";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio74";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_mi2s {
+ tert_mi2s_sleep: tert_mi2s_sleep {
+ mux {
+ pins = "gpio75", "gpio76";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio75", "gpio76";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_mi2s_active: tert_mi2s_active {
+ mux {
+ pins = "gpio75", "gpio76";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio75", "gpio76";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ tert_mi2s_sd0 {
+ tert_mi2s_sd0_sleep: tert_mi2s_sd0_sleep {
+ mux {
+ pins = "gpio77";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio77";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_mi2s_sd0_active: tert_mi2s_sd0_active {
+ mux {
+ pins = "gpio77";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio77";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_mi2s_sd1 {
+ tert_mi2s_sd1_sleep: tert_mi2s_sd1_sleep {
+ mux {
+ pins = "gpio78";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio78";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_mi2s_sd1_active: tert_mi2s_sd1_active {
+ mux {
+ pins = "gpio78";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio78";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s_mclk {
+ quat_mi2s_mclk_sleep: quat_mi2s_mclk_sleep {
+ mux {
+ pins = "gpio57";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio57";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_mclk_active: quat_mi2s_mclk_active {
+ mux {
+ pins = "gpio57";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio57";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s {
+ quat_mi2s_sleep: quat_mi2s_sleep {
+ mux {
+ pins = "gpio58", "gpio59";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio58", "gpio59";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_active: quat_mi2s_active {
+ mux {
+ pins = "gpio58", "gpio59";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio58", "gpio59";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ quat_mi2s_sd0 {
+ quat_mi2s_sd0_sleep: quat_mi2s_sd0_sleep {
+ mux {
+ pins = "gpio60";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio60";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_sd0_active: quat_mi2s_sd0_active {
+ mux {
+ pins = "gpio60";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio60";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s_sd1 {
+ quat_mi2s_sd1_sleep: quat_mi2s_sd1_sleep {
+ mux {
+ pins = "gpio61";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio61";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_sd1_active: quat_mi2s_sd1_active {
+ mux {
+ pins = "gpio61";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio61";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s_sd2 {
+ quat_mi2s_sd2_sleep: quat_mi2s_sd2_sleep {
+ mux {
+ pins = "gpio62";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio62";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_sd2_active: quat_mi2s_sd2_active {
+ mux {
+ pins = "gpio62";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio62";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s_sd3 {
+ quat_mi2s_sd3_sleep: quat_mi2s_sd3_sleep {
+ mux {
+ pins = "gpio63";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio63";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_sd3_active: quat_mi2s_sd3_active {
+ mux {
+ pins = "gpio63";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio63";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ spkr_i2s_clk_pin {
+ spkr_i2s_clk_sleep: spkr_i2s_clk_sleep {
+ mux {
+ pins = "gpio69";
+ function = "spkr_i2s";
+ };
+
+ config {
+ pins = "gpio69";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ };
+ };
+
+ spkr_i2s_clk_active: spkr_i2s_clk_active {
+ mux {
+ pins = "gpio69";
+ function = "spkr_i2s";
+ };
+
+ config {
+ pins = "gpio69";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ spkr_1_sd_mediabox {
+ spkr_1_sd_sleep_mediabox: spkr_1_sd_sleep_mediabox {
+ mux {
+ pins = "gpio85";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio85";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ input-enable;
+ };
+ };
+ spkr_1_sd_active_mediabox: spkr_1_sd_active_mediabox {
+ mux {
+ pins = "gpio85";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio85";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ spkr_2_sd_mediabox_mediabox {
+ spkr_2_sd_sleep_mediabox: spkr_2_sd_sleep_mediabox {
+ mux {
+ pins = "gpio112";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio112";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ input-enable;
+ };
+ };
+ spkr_2_sd_active_mediabox: spkr_2_sd_active_mediabox {
+ mux {
+ pins = "gpio112";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio112";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ sdc2_cd_on_mediabox: sdc2_cd_on_mediabox {
+ mux {
+ pins = "gpio86";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio86";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc2_cd_off_mediabox: sdc2_cd_off_mediabox {
+ mux {
+ pins = "gpio86";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio86";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ ir_active: ir_active {
+ mux {
+ pins = "gpio90", "gpio91";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio90", "gpio91";
+ drive-strength = <16>;
+ bias-disable;
+ output-high;
+ };
+ };
+
+ mtch6102_int {
+ mtch6102_int_active: mtch6102_int_active {
+ mux {
+ pins = "gpio125";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio125";
+ drive-strength = <16>; /* 16 mA */
+ bias-pull-up;
+ };
+ };
+
+ mtch6102_int_suspend: mtch6102_int_suspend {
+ mux {
+ pins = "gpio125";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio125";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ };
+ };
+ };
+
+ mtch6102_rst {
+ mtch6102_rst_active: mtch6102_rst_active {
+ mux {
+ pins = "gpio85";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio85";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ mtch6102_rst_suspend: mtch6102_rst_suspend {
+ mux {
+ pins = "gpio85";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio85";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-svr20.dtsi b/arch/arm/boot/dts/qcom/msm8998-svr20.dtsi
new file mode 100644
index 000000000000..a1fce88c8e54
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-svr20.dtsi
@@ -0,0 +1,409 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include "msm8998-svr20-pinctrl.dtsi"
+#include "msm8998-camera-sensor-svr20.dtsi"
+&vendor {
+ bluetooth: bt_wcn3990 {
+ compatible = "qca,wcn3990";
+ qca,bt-vdd-io-supply = <&pm8998_s3>;
+ qca,bt-vdd-xtal-supply = <&pm8998_s5>;
+ qca,bt-vdd-core-supply = <&pm8998_l7>;
+ qca,bt-vdd-pa-supply = <&pm8998_l17>;
+ qca,bt-vdd-ldo-supply = <&pm8998_l25>;
+ qca,bt-chip-pwd-supply = <&pmi8998_bob_pin1>;
+ clocks = <&clock_gcc clk_rf_clk2_pin>;
+ clock-names = "rf_clk2";
+
+ qca,bt-vdd-io-voltage-level = <1352000 1352000>;
+ qca,bt-vdd-xtal-voltage-level = <2040000 2040000>;
+ qca,bt-vdd-core-voltage-level = <1800000 1800000>;
+ qca,bt-vdd-pa-voltage-level = <1304000 1304000>;
+ qca,bt-vdd-ldo-voltage-level = <3312000 3312000>;
+ qca,bt-chip-pwd-voltage-level = <3600000 3600000>;
+
+ qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
+ };
+ svr20_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <25>;
+ #include "fg-gen3-batterydata-svr-v2-3200mah.dtsi"
+ };
+};
+
+&blue_led {
+ qcom,default-state = "on";
+ linux,default-trigger = "system-running";
+};
+
+&pmi8998_charger {
+ qcom,fcc-max-ua = <5000000>;
+ qcom,usb-icl-ua = <3000000>;
+};
+
+&blsp1_uart3_hs {
+ status = "ok";
+};
+
+&ufsphy1 {
+ vdda-phy-supply = <&pm8998_l1>;
+ vdda-pll-supply = <&pm8998_l2>;
+ vddp-ref-clk-supply = <&pm8998_l26>;
+ vdda-phy-max-microamp = <51400>;
+ vdda-pll-max-microamp = <14600>;
+ vddp-ref-clk-max-microamp = <100>;
+ vddp-ref-clk-always-on;
+ status = "ok";
+};
+
+&ufs1 {
+ vdd-hba-supply = <&gdsc_ufs>;
+ vdd-hba-fixed-regulator;
+ vcc-supply = <&pm8998_l20>;
+ vccq-supply = <&pm8998_l26>;
+ vccq2-supply = <&pm8998_s4>;
+ vcc-max-microamp = <750000>;
+ vccq-max-microamp = <560000>;
+ vccq2-max-microamp = <750000>;
+ status = "ok";
+};
+
+&ufs_ice {
+ status = "ok";
+};
+
+&sdhc_2 {
+ vdd-supply = <&pm8998_l21>;
+ qcom,vdd-voltage-level = <2950000 2960000>;
+ qcom,vdd-current-level = <200 800000>;
+
+ vdd-io-supply = <&pm8998_l13>;
+ qcom,vdd-io-voltage-level = <1808000 2960000>;
+ qcom,vdd-io-current-level = <200 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000
+ 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ cd-gpios = <&tlmm 95 0x1>;
+
+ status = "ok";
+};
+
+&uartblsp2dm1 {
+ status = "ok";
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart_console_active>;
+};
+
+&pm8998_gpios {
+ /* GPIO 5 for Home Key */
+ gpio@c400 {
+ status = "okay";
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,out-strength = <1>;
+ };
+
+ /* GPIO 6 for Vol+ Key */
+ gpio@c500 {
+ status = "okay";
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,out-strength = <1>;
+ };
+
+ /* GPIO 7 for Snapshot Key */
+ gpio@c600 {
+ status = "okay";
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,out-strength = <1>;
+ };
+
+ /* GPIO 8 for Focus Key */
+ gpio@c700 {
+ status = "okay";
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,out-strength = <1>;
+ };
+
+ gpio@cc00 { /* GPIO 13 */
+ qcom,mode = <1>;
+ qcom,output-type = <0>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <0>;
+ qcom,out-strength = <1>;
+ qcom,src-sel = <3>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+
+ /* GPIO 21 (NFC_CLK_REQ) */
+ gpio@d400 {
+ qcom,mode = <0>;
+ qcom,vin-sel = <1>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+
+ /* GPIO 18 SMB138X */
+ gpio@d100 {
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+};
+
+&i2c_5 {
+ status = "okay";
+};
+
+&i2c_6 { /* BLSP1 QUP6 (NFC) */
+ status = "okay";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 92 0x00>;
+ qcom,nq-ven = <&tlmm 12 0x00>;
+ qcom,nq-firm = <&tlmm 93 0x00>;
+ qcom,nq-clkreq = <&pm8998_gpios 21 0x00>;
+ qcom,nq-esepwr = <&tlmm 116 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <92 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_enable_active>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+ clocks = <&clock_gcc clk_ln_bb_clk3_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
+&mdss_hdmi_tx {
+ status = "disabled";
+ pinctrl-names = "hdmi_hpd_active", "hdmi_ddc_active", "hdmi_cec_active",
+ "hdmi_active", "hdmi_sleep";
+ pinctrl-0 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active
+ &mdss_hdmi_ddc_suspend &mdss_hdmi_cec_suspend>;
+ pinctrl-1 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active
+ &mdss_hdmi_ddc_active &mdss_hdmi_cec_suspend>;
+ pinctrl-2 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active
+ &mdss_hdmi_cec_active &mdss_hdmi_ddc_suspend>;
+ pinctrl-3 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active
+ &mdss_hdmi_ddc_active &mdss_hdmi_cec_active>;
+ pinctrl-4 = <&mdss_hdmi_5v_suspend &mdss_hdmi_hpd_suspend
+ &mdss_hdmi_ddc_suspend &mdss_hdmi_cec_suspend>;
+};
+
+&mdss_dp_ctrl {
+ status = "disabled";
+ pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
+ pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>;
+ pinctrl-1 = <&mdss_dp_aux_suspend &mdss_dp_usbplug_cc_suspend>;
+ qcom,aux-en-gpio = <&tlmm 77 0>;
+ qcom,aux-sel-gpio = <&tlmm 78 0>;
+ qcom,usbplug-cc-gpio = <&tlmm 38 0>;
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "split_dsi";
+};
+
+&mem_client_3_size {
+ qcom,peripheral-size = <0x500000>;
+};
+
+&pmi8998_haptics {
+ status = "okay";
+};
+
+&pm8998_vadc {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@85 {
+ label = "vcoin";
+ reg = <0x85>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+};
+
+&pm8998_adc_tm {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,btm-channel-number = <0x60>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x68>;
+ qcom,thermal-node;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x70>;
+ qcom,thermal-node;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x78>;
+ qcom,thermal-node;
+ };
+};
+
+&wil6210 {
+ status = "ok";
+};
+
+&soc {
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ status = "okay";
+
+ home {
+ label = "home";
+ gpios = <&pm8998_gpios 5 0x1>;
+ linux,input-type = <1>;
+ linux,code = <158>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&pm8998_gpios 6 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_down {
+ label = "volume_down";
+ gpios = <&pm8998_gpios 7 0x1>;
+ linux,input-type = <1>;
+ linux,code = <114>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ confirm {
+ label = "confirm_key";
+ gpios = <&pm8998_gpios 8 0x1>;
+ linux,input-type = <1>;
+ linux,code = <28>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
+};
+
+&pmi8998_fg {
+ qcom,battery-data = <&svr20_batterydata>;
+ qcom,fg-force-load-profile;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi
index b2f30de94bbc..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 >;
};
};
@@ -436,7 +437,7 @@
0x9ac 0x00 0x00
0x8a0 0x01 0x00
0x9e0 0x00 0x00
- 0x9dc 0x01 0x00
+ 0x9dc 0x20 0x00
0x9a8 0x00 0x00
0x8a4 0x01 0x00
0x8a8 0x73 0x00
diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi
index 3f8962de22be..eafa6b841c17 100644
--- a/arch/arm/boot/dts/qcom/msm8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998.dtsi
@@ -2675,7 +2675,7 @@
0x9ac 0x00 0x00
0x8a0 0x01 0x00
0x9e0 0x00 0x00
- 0x9dc 0x01 0x00
+ 0x9dc 0x20 0x00
0x9a8 0x00 0x00
0x8a4 0x01 0x00
0x8a8 0x73 0x00
diff --git a/arch/arm/boot/dts/qcom/sda630-cdp.dts b/arch/arm/boot/dts/qcom/sda630-cdp.dts
index 665fa94d9713..beefb2de1ce9 100644
--- a/arch/arm/boot/dts/qcom/sda630-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sda630-cdp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sda630-cdp", "qcom,sda630", "qcom,cdp";
qcom,board-id = <1 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sda630-mtp.dts b/arch/arm/boot/dts/qcom/sda630-mtp.dts
index 08a996ddb709..41afa720c389 100644
--- a/arch/arm/boot/dts/qcom/sda630-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sda630-mtp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sda630-mtp", "qcom,sda630", "qcom,mtp";
qcom,board-id = <8 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sda630-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sda630-pm660a-cdp.dts
index 6094d22c1c92..01455d054e03 100644
--- a/arch/arm/boot/dts/qcom/sda630-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sda630-pm660a-cdp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDA 630 PM660 + PM660A CDP";
compatible = "qcom,sda630-cdp", "qcom,sda630", "qcom,cdp";
qcom,board-id = <1 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sda630-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sda630-pm660a-mtp.dts
index 49c10129aada..3dd15d889afd 100644
--- a/arch/arm/boot/dts/qcom/sda630-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sda630-pm660a-mtp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDA 630 PM660 + PM660A MTP";
compatible = "qcom,sda630-mtp", "qcom,sda630", "qcom,mtp";
qcom,board-id = <8 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts
index 4c4c758daa29..155099b5be9d 100644
--- a/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts
+++ b/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDA 630 PM660 + PM660A QRD HDK630";
compatible = "qcom,sda630-qrd", "qcom,sda630", "qcom,qrd";
qcom,board-id = <0x0016000b 0x00>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&pm660a_oledb {
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..2962a6bc3691
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sda636-cdp.dts
@@ -0,0 +1,37 @@
+/* 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>,
+ <0x0001001b 0x0102001a 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..237d27791b94
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sda636-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 "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>,
+ <0x0001001b 0x0102001a 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..5f0e5275e828
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sda636-pm660a-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 "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>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 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..c15bc159dffd
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sda636-pm660a-mtp.dts
@@ -0,0 +1,32 @@
+/* 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>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 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..f4a9592bf4ff
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sda636-pm660a-qrd-hdk.dts
@@ -0,0 +1,242 @@
+/* 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>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 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/msm8998-9x55.dtsi b/arch/arm/boot/dts/qcom/sda636.dtsi
index be947507e398..a219ca92767a 100644
--- a/arch/arm/boot/dts/qcom/msm8998-9x55.dtsi
+++ b/arch/arm/boot/dts/qcom/sda636.dtsi
@@ -1,5 +1,4 @@
-/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* 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
@@ -11,15 +10,20 @@
* GNU General Public License for more details.
*/
-
-#include "skeleton64.dtsi"
-#include "msm8998-v2.1.dtsi"
+#include "sdm636.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. MSM8998-9x55";
- compatible = "qcom,msm8998-9x55";
- qcom,msm-id = <292 0x0>;
- interrupt-parent = <&intc>;
+ model = "Qualcomm Technologies, Inc. SDA 636";
+ compatible = "qcom,sda636";
+ qcom,msm-id = <346 0x0>;
+};
+
+&soc {
+ qcom,rmnet-ipa {
+ status = "disabled";
+ };
+};
- soc: soc { };
+&ipa_hw {
+ status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/sda658-cdp.dts b/arch/arm/boot/dts/qcom/sda658-cdp.dts
index 9992963b8705..5db30e379a2d 100644
--- a/arch/arm/boot/dts/qcom/sda658-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sda658-cdp.dts
@@ -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
@@ -21,5 +21,6 @@
compatible = "qcom,sda658-cdp", "qcom,sda658", "qcom,cdp";
qcom,board-id = <1 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sda658-mtp.dts b/arch/arm/boot/dts/qcom/sda658-mtp.dts
index f4322ecfd701..138c6fdc74df 100644
--- a/arch/arm/boot/dts/qcom/sda658-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sda658-mtp.dts
@@ -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
@@ -21,5 +21,6 @@
compatible = "qcom,sda658-mtp", "qcom,sda658", "qcom,mtp";
qcom,board-id = <8 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sda658-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sda658-pm660a-cdp.dts
index c280c4afda51..b4e7996e0bb6 100644
--- a/arch/arm/boot/dts/qcom/sda658-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sda658-pm660a-cdp.dts
@@ -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
@@ -21,5 +21,7 @@
model = "Qualcomm Technologies, Inc. SDA 658 PM660 + PM660A CDP";
compatible = "qcom,sda658-cdp", "qcom,sda658", "qcom,cdp";
qcom,board-id = <1 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sda658-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sda658-pm660a-mtp.dts
index ba8741e2a068..52820a557ecf 100644
--- a/arch/arm/boot/dts/qcom/sda658-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sda658-pm660a-mtp.dts
@@ -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
@@ -21,5 +21,7 @@
model = "Qualcomm Technologies, Inc. SDA 658 PM660 + PM660A MTP";
compatible = "qcom,sda658-mtp", "qcom,sda658", "qcom,mtp";
qcom,board-id = <8 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sda660-cdp.dts b/arch/arm/boot/dts/qcom/sda660-cdp.dts
index 92097729087b..4299a0957455 100644
--- a/arch/arm/boot/dts/qcom/sda660-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sda660-cdp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sda660-cdp", "qcom,sda660", "qcom,cdp";
qcom,board-id = <1 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sda660-mtp.dts b/arch/arm/boot/dts/qcom/sda660-mtp.dts
index 027137e54c0e..71263375b1c0 100644
--- a/arch/arm/boot/dts/qcom/sda660-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sda660-mtp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sda660-mtp", "qcom,sda660", "qcom,mtp";
qcom,board-id = <8 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sda660-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sda660-pm660a-cdp.dts
index a46083a00298..38900dab7596 100644
--- a/arch/arm/boot/dts/qcom/sda660-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sda660-pm660a-cdp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDA 660 PM660 + PM660A CDP";
compatible = "qcom,sda660-cdp", "qcom,sda660", "qcom,cdp";
qcom,board-id = <1 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sda660-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sda660-pm660a-mtp.dts
index d94cf8ea1eb5..98ab098d6cdc 100644
--- a/arch/arm/boot/dts/qcom/sda660-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sda660-pm660a-mtp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDA 660 PM660 + PM660A MTP";
compatible = "qcom,sda660-mtp", "qcom,sda660", "qcom,mtp";
qcom,board-id = <8 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts
index 0f4b462fd57b..5f44b4c32c98 100644
--- a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts
+++ b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts
@@ -158,7 +158,9 @@
model = "Qualcomm Technologies, Inc. SDA 660 PM660 + PM660A QRD HDK660";
compatible = "qcom,sda660-qrd", "qcom,sda660", "qcom,qrd";
qcom,board-id = <0x0016000b 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&pm660a_oledb {
diff --git a/arch/arm/boot/dts/qcom/sdm630-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm630-camera-sensor-mtp.dtsi
index 0275016c9662..32a251f00550 100644
--- a/arch/arm/boot/dts/qcom/sdm630-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-camera-sensor-mtp.dtsi
@@ -114,6 +114,26 @@
status = "disabled";
};
+ tof0: qcom,tof@29{
+ cell-index = <0>;
+ reg = <0x29>;
+ compatible = "st,stmvl53l0";
+ qcom,cci-master = <0>;
+ cam_laser-supply = <&pm660l_l8>;
+ qcom,cam-vreg-name = "cam_laser";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <3400000>;
+ qcom,cam-vreg-op-mode = <100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_tof_active>;
+ pinctrl-1 = <&cam_tof_suspend>;
+ stm,irq-gpio = <&tlmm 45 0x2008>;
+ gpios = <&tlmm 42 0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "RNG_EN";
+ };
+
eeprom0: qcom,eeprom@0 {
cell-index = <0>;
reg = <0>;
diff --git a/arch/arm/boot/dts/qcom/sdm630-cdp.dts b/arch/arm/boot/dts/qcom/sdm630-cdp.dts
index 973df0df3be5..8da3dd6726e6 100644
--- a/arch/arm/boot/dts/qcom/sdm630-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-cdp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sdm630-cdp", "qcom,sdm630", "qcom,cdp";
qcom,board-id = <1 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sdm630-headset-jacktype-no-cdp.dts b/arch/arm/boot/dts/qcom/sdm630-headset-jacktype-no-cdp.dts
index 4db377dc755a..27ef94e8a29a 100644
--- a/arch/arm/boot/dts/qcom/sdm630-headset-jacktype-no-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-headset-jacktype-no-cdp.dts
@@ -22,5 +22,6 @@
compatible = "qcom,sdm630-cdp", "qcom,sdm630", "qcom,cdp";
qcom,board-id = <1 2>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-internal-codec-cdp.dts b/arch/arm/boot/dts/qcom/sdm630-internal-codec-cdp.dts
index baa55fa15160..39099ed1e97e 100644
--- a/arch/arm/boot/dts/qcom/sdm630-internal-codec-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-internal-codec-cdp.dts
@@ -22,5 +22,6 @@
compatible = "qcom,sdm630-cdp", "qcom,sdm630", "qcom,cdp";
qcom,board-id = <1 1>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-internal-codec-mtp.dts b/arch/arm/boot/dts/qcom/sdm630-internal-codec-mtp.dts
index b469a59d7818..d9f30b41ed8e 100644
--- a/arch/arm/boot/dts/qcom/sdm630-internal-codec-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-internal-codec-mtp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sdm630-mtp", "qcom,sdm630", "qcom,mtp";
qcom,board-id = <8 1>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&int_codec {
diff --git a/arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-cdp.dts
index c4e71835b701..0ec2e17f10a5 100644
--- a/arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-cdp.dts
@@ -22,5 +22,7 @@
model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A Int. Audio Codec CDP";
compatible = "qcom,sdm630-cdp", "qcom,sdm630", "qcom,cdp";
qcom,board-id = <1 1>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-mtp.dts
index e11cdfbed668..d4f1214dc191 100644
--- a/arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-internal-codec-pm660a-mtp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A Int. Audio Codec MTP";
compatible = "qcom,sdm630-mtp", "qcom,sdm630", "qcom,mtp";
qcom,board-id = <8 1>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&int_codec {
diff --git a/arch/arm/boot/dts/qcom/sdm630-mtp.dts b/arch/arm/boot/dts/qcom/sdm630-mtp.dts
index b1a9bb86149d..3604ce486633 100644
--- a/arch/arm/boot/dts/qcom/sdm630-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-mtp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sdm630-mtp", "qcom,sdm630", "qcom,mtp";
qcom,board-id = <8 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sdm630-pm.dtsi b/arch/arm/boot/dts/qcom/sdm630-pm.dtsi
index f10477bab8cf..f39f8b880690 100644
--- a/arch/arm/boot/dts/qcom/sdm630-pm.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-pm.dtsi
@@ -24,7 +24,7 @@
qcom,vctl-timeout-us = <500>;
qcom,vctl-port = <0x0>;
qcom,phase-port = <0x1>;
- qcom,saw2-avs-ctl = <0x101c031>;
+ qcom,saw2-avs-ctl = <0x1010031>;
qcom,saw2-avs-limit = <0x4580458>;
qcom,pfm-port = <0x2>;
};
@@ -40,7 +40,7 @@
qcom,vctl-timeout-us = <500>;
qcom,vctl-port = <0x0>;
qcom,phase-port = <0x1>;
- qcom,saw2-avs-ctl = <0x101c031>;
+ qcom,saw2-avs-ctl = <0x1010031>;
qcom,saw2-avs-limit = <0x4580458>;
qcom,pfm-port = <0x2>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts
index 7e3e9a0cca59..a6f01eaa2a43 100644
--- a/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A CDP";
compatible = "qcom,sdm630-cdp", "qcom,sdm630", "qcom,cdp";
qcom,board-id = <1 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-headset-jacktype-no-cdp.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-headset-jacktype-no-cdp.dts
index 15936f47da7b..1596f7533581 100644
--- a/arch/arm/boot/dts/qcom/sdm630-pm660a-headset-jacktype-no-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-headset-jacktype-no-cdp.dts
@@ -22,5 +22,7 @@
model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A, Headset Jacktype NO, CDP";
compatible = "qcom,sdm630-cdp", "qcom,sdm630", "qcom,cdp";
qcom,board-id = <1 2>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts
index a522b7ad1d5f..418b4a1e819f 100644
--- a/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A MTP";
compatible = "qcom,sdm630-mtp", "qcom,sdm630", "qcom,mtp";
qcom,board-id = <8 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts
index deb10b591444..95b03f68b30f 100644
--- a/arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A QRD";
compatible = "qcom,sdm630-qrd", "qcom,sdm630", "qcom,qrd";
qcom,board-id = <0x0002000b 0x00>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&int_codec {
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-usbc-audio-mtp.dts b/arch/arm/boot/dts/qcom/sdm630-usbc-audio-mtp.dts
index eb089b524fef..309e9c6730a9 100644
--- a/arch/arm/boot/dts/qcom/sdm630-usbc-audio-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-usbc-audio-mtp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sdm630-mtp", "qcom,sdm630", "qcom,mtp";
qcom,board-id = <8 2>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi
index 1a95427c5c66..e918864a3df7 100644
--- a/arch/arm/boot/dts/qcom/sdm630.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630.dtsi
@@ -613,6 +613,7 @@
compatible = "qcom,memshare-peripheral";
qcom,peripheral-size = <0x0>;
qcom,client-id = <1>;
+ qcom,allocate-boot-time;
label = "modem";
};
};
@@ -1305,18 +1306,9 @@
compatible = "qcom,clk-cpu-osm-sdm630";
reg = <0x179c0000 0x4000>, <0x17916000 0x1000>,
<0x17816000 0x1000>, <0x179d1000 0x1000>,
- <0x00784130 0x8>, <0x17914800 0x800>,
- <0x17814800 0x800>;
+ <0x00784130 0x8>;
reg-names = "osm", "pwrcl_pll", "perfcl_pll",
- "apcs_common", "perfcl_efuse",
- "pwrcl_acd", "perfcl_acd";
-
- qcom,acdtd-val = <0x0000a111 0x0000a111>;
- qcom,acdcr-val = <0x002c5ffd 0x002c5ffd>;
- qcom,acdsscr-val = <0x00000901 0x00000901>;
- qcom,acdextint0-val = <0x2cf9ae8 0x2cf9ae8>;
- qcom,acdextint1-val = <0x2cf9afe 0x2cf9afe>;
- qcom,acdautoxfer-val = <0x00000015 0x00000015>;
+ "apcs_common", "perfcl_efuse";
vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
vdd-perfcl-supply = <&apc1_perfcl_vreg>;
@@ -1374,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>;
@@ -2040,6 +2031,7 @@
qcom,qsee-ce-hw-instance = <0>;
qcom,disk-encrypt-pipe-pair = <2>;
qcom,support-fde;
+ qcom,fde-key-size;
qcom,no-clock-support;
qcom,msm-bus,name = "qseecom-noc";
qcom,msm-bus,num-cases = <4>;
diff --git a/arch/arm/boot/dts/qcom/sdm636-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm636-camera-sensor-mtp.dtsi
new file mode 100644
index 000000000000..ab6a7aebd6b9
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-camera-sensor-mtp.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.
+ */
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&pm660l_l8>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <3400000>;
+ qcom,cam-vreg-op-mode = <100000>;
+ };
+
+ actuator1: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ cam_vaf-supply = <&pm660l_l8>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <3400000>;
+ qcom,cam-vreg-op-mode = <100000>;
+ };
+
+ actuator2: qcom,actuator@2 {
+ cell-index = <2>;
+ reg = <0x2>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ cam_vaf-supply = <&pm660l_l8>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <3400000>;
+ qcom,cam-vreg-op-mode = <100000>;
+ };
+};
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..ad1cac9c0c90
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-cdp.dts
@@ -0,0 +1,37 @@
+/* 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>,
+ <0x0001001b 0x0102001a 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/msm8998-9x55-mtp.dts b/arch/arm/boot/dts/qcom/sdm636-cdp.dtsi
index a95e9e4f272f..279a542be7e4 100644
--- a/arch/arm/boot/dts/qcom/msm8998-9x55-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm636-cdp.dtsi
@@ -1,5 +1,4 @@
-/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* 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
@@ -11,14 +10,18 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+#include "sdm660-cdp.dtsi"
+/ {
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
-#include "msm8998-9x55.dtsi"
-#include "msm8998-mdss-panels.dtsi"
-#include "msm8998-mtp.dtsi"
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_nt35695b_truly_fhd_video>;
+};
-/ {
- model = "Qualcomm Technologies, Inc. MSM8998-9x55 MTP";
- compatible = "qcom,msm8998-9x55-mtp", "qcom,msm8998-9x55", "qcom,mtp";
- qcom,board-id= <8 6>;
+&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..c87ace6938bd
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-headset-jacktype-no-cdp.dts
@@ -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.
+ */
+
+
+/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>,
+ <0x0001001b 0x0102001a 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..1de6c4c13300
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-cdp.dts
@@ -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.
+ */
+
+
+/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>,
+ <0x0001001b 0x0102001a 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..3522e67cf87e
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-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-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>,
+ <0x0001001b 0x0102001a 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..e51c2e4709d2
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-cdp.dts
@@ -0,0 +1,28 @@
+/* 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>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 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..e4d980a15a0d
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-internal-codec-pm660a-mtp.dts
@@ -0,0 +1,33 @@
+/* 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>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 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..8ee5c46c804a
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-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 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>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
+};
+
+&tavil_snd {
+ qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-9x55-rcm.dts b/arch/arm/boot/dts/qcom/sdm636-mtp.dtsi
index 094ecbc50061..30174df3bd6a 100644
--- a/arch/arm/boot/dts/qcom/msm8998-9x55-rcm.dts
+++ b/arch/arm/boot/dts/qcom/sdm636-mtp.dtsi
@@ -1,5 +1,4 @@
-/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* 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
@@ -11,14 +10,19 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+#include "sdm660-mtp.dtsi"
+#include "sdm636-camera-sensor-mtp.dtsi"
+/ {
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
-#include "msm8998-9x55.dtsi"
-#include "msm8998-mdss-panels.dtsi"
-#include "msm8998-cdp.dtsi"
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_nt35695b_truly_fhd_video>;
+};
-/ {
- model = "Qualcomm Technologies, Inc. MSM8998-9x55 RCM";
- compatible = "qcom,msm8998-9x55-cdp", "qcom,msm8998-9x55", "qcom,cdp";
- qcom,board-id= <0x21 2>;
+&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..88d19d0c7bb1
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-cdp.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-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>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 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..b60fdc773941
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-headset-jacktype-no-cdp.dts
@@ -0,0 +1,40 @@
+/* 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>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 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..6aaa8d070b42
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-mtp.dts
@@ -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.
+ */
+
+
+/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>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 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..68734c9cd8c6
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-pm660a-qrd.dts
@@ -0,0 +1,58 @@
+/* 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>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 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..b1977cdc50bc
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-qrd.dts
@@ -0,0 +1,88 @@
+/* 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>,
+ <0x0001001b 0x0102001a 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..8519a4891446
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm636-usbc-audio-mtp.dts
@@ -0,0 +1,32 @@
+/* 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>,
+ <0x0001001b 0x0102001a 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/sdm658-cdp.dts b/arch/arm/boot/dts/qcom/sdm658-cdp.dts
index 8569af157049..223177aece50 100644
--- a/arch/arm/boot/dts/qcom/sdm658-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-cdp.dts
@@ -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
@@ -21,5 +21,6 @@
compatible = "qcom,sdm658-cdp", "qcom,sdm658", "qcom,cdp";
qcom,board-id = <1 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm658-internal-codec-cdp.dts b/arch/arm/boot/dts/qcom/sdm658-internal-codec-cdp.dts
index d0f5c14223ff..c6cdf6e797af 100644
--- a/arch/arm/boot/dts/qcom/sdm658-internal-codec-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-internal-codec-cdp.dts
@@ -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
@@ -21,7 +21,8 @@
compatible = "qcom,sdm658-cdp", "qcom,sdm658", "qcom,cdp";
qcom,board-id = <1 1>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&slim_aud {
diff --git a/arch/arm/boot/dts/qcom/sdm658-internal-codec-mtp.dts b/arch/arm/boot/dts/qcom/sdm658-internal-codec-mtp.dts
index acec15e0615f..32203c9ccaee 100644
--- a/arch/arm/boot/dts/qcom/sdm658-internal-codec-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-internal-codec-mtp.dts
@@ -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
@@ -21,7 +21,8 @@
compatible = "qcom,sdm658-mtp", "qcom,sdm658", "qcom,mtp";
qcom,board-id = <8 1>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&slim_aud {
diff --git a/arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-cdp.dts
index b7f2e70ce962..a8d3de6f8962 100644
--- a/arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-cdp.dts
@@ -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
@@ -21,5 +21,7 @@
model = "Qualcomm Technologies, Inc. SDM 658 PM660 + PM660A Int. Audio Codec CDP";
compatible = "qcom,sdm658-cdp", "qcom,sdm658", "qcom,cdp";
qcom,board-id = <1 1>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-mtp.dts
index 949d7ae7faa5..2689dc099f12 100644
--- a/arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-internal-codec-pm660a-mtp.dts
@@ -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
@@ -21,5 +21,7 @@
model = "Qualcomm Technologies, Inc. SDM 658 PM660 + PM660A Int. Audio Codec MTP";
compatible = "qcom,sdm658-mtp", "qcom,sdm658", "qcom,mtp";
qcom,board-id = <8 1>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm658-mtp.dts b/arch/arm/boot/dts/qcom/sdm658-mtp.dts
index 2fbe9b0a6201..a57bba3b22af 100644
--- a/arch/arm/boot/dts/qcom/sdm658-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-mtp.dts
@@ -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
@@ -21,5 +21,6 @@
compatible = "qcom,sdm658-mtp", "qcom,sdm658", "qcom,mtp";
qcom,board-id = <8 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm658-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm658-pm660a-cdp.dts
index 39e2df958347..c719d76613b0 100644
--- a/arch/arm/boot/dts/qcom/sdm658-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-pm660a-cdp.dts
@@ -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
@@ -21,5 +21,7 @@
model = "Qualcomm Technologies, Inc. SDM 658 PM660 + PM660A CDP";
compatible = "qcom,sdm658-cdp", "qcom,sdm658", "qcom,cdp";
qcom,board-id = <1 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm658-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm658-pm660a-mtp.dts
index 4d205ef403f4..4b6b7fa23710 100644
--- a/arch/arm/boot/dts/qcom/sdm658-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-pm660a-mtp.dts
@@ -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
@@ -21,5 +21,7 @@
model = "Qualcomm Technologies, Inc. SDM 658 PM660 + PM660A MTP";
compatible = "qcom,sdm658-mtp", "qcom,sdm658", "qcom,mtp";
qcom,board-id = <8 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm658-pm660a-qrd.dts b/arch/arm/boot/dts/qcom/sdm658-pm660a-qrd.dts
index f1edf6b244a8..cd51a74ba18c 100644
--- a/arch/arm/boot/dts/qcom/sdm658-pm660a-qrd.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-pm660a-qrd.dts
@@ -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
@@ -21,5 +21,7 @@
model = "Qualcomm Technologies, Inc. SDM 658 PM660 + PM660A QRD";
compatible = "qcom,sdm658-qrd", "qcom,sdm658", "qcom,qrd";
qcom,board-id = <0x1000b 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm658-qrd.dts b/arch/arm/boot/dts/qcom/sdm658-qrd.dts
index bd7d76ee1f6c..155b730d52c3 100644
--- a/arch/arm/boot/dts/qcom/sdm658-qrd.dts
+++ b/arch/arm/boot/dts/qcom/sdm658-qrd.dts
@@ -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
@@ -21,5 +21,6 @@
compatible = "qcom,sdm658-qrd", "qcom,sdm658", "qcom,qrd";
qcom,board-id = <0x1000b 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi
index 46f77e9a3253..476fedec35a4 100644
--- a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi
@@ -29,6 +29,16 @@
qcom,switch-source = <&pm660l_switch1>;
status = "ok";
};
+
+ cam_actuator_regulator: cam_actuator_fixed_regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "cam_actuator_regulator";
+ regulator-min-microvolt = <3600000>;
+ regulator-max-microvolt = <3600000>;
+ enable-active-high;
+ gpio = <&tlmm 50 0>;
+ vin-supply = <&pm660l_bob>;
+ };
};
&cci {
@@ -37,14 +47,11 @@
reg = <0x0>;
compatible = "qcom,actuator";
qcom,cci-master = <0>;
- gpios = <&tlmm 50 0>;
- qcom,gpio-vaf = <0>;
- qcom,gpio-req-tbl-num = <0>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_VAF";
- pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_actuator_vaf_active>;
- pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ cam_vaf-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ qcom,cam-vreg-op-mode = <0>;
};
actuator1: qcom,actuator@1 {
@@ -52,14 +59,11 @@
reg = <0x1>;
compatible = "qcom,actuator";
qcom,cci-master = <1>;
- gpios = <&tlmm 50 0>;
- qcom,gpio-vaf = <0>;
- qcom,gpio-req-tbl-num = <0>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_VAF";
- pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_actuator_vaf_active>;
- pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ cam_vaf-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ qcom,cam-vreg-op-mode = <0>;
};
actuator2: qcom,actuator@2 {
@@ -67,14 +71,11 @@
reg = <0x2>;
compatible = "qcom,actuator";
qcom,cci-master = <1>;
- gpios = <&tlmm 50 0>;
- qcom,gpio-vaf = <0>;
- qcom,gpio-req-tbl-num = <0>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_VAF";
- pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_actuator_vaf_active>;
- pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ cam_vaf-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ qcom,cam-vreg-op-mode = <0>;
};
ois0: qcom,ois@0 {
@@ -82,15 +83,31 @@
reg = <0x0>;
compatible = "qcom,ois";
qcom,cci-master = <0>;
- gpios = <&tlmm 50 0>;
- qcom,gpio-vaf = <0>;
+ cam_vaf-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ qcom,cam-vreg-op-mode = <0>;
+ status = "disabled";
+ };
+
+ tof0: qcom,tof@29{
+ cell-index = <0>;
+ reg = <0x29>;
+ compatible = "st,stmvl53l0";
+ qcom,cci-master = <0>;
+ cam_laser-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_laser";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_tof_active>;
+ pinctrl-1 = <&cam_tof_suspend>;
+ stm,irq-gpio = <&tlmm 45 0x2008>;
+ gpios = <&tlmm 42 0>;
qcom,gpio-req-tbl-num = <0>;
qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_VAF";
- pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_actuator_vaf_active>;
- pinctrl-1 = <&cam_actuator_vaf_suspend>;
- status = "disabled";
+ qcom,gpio-req-tbl-label = "RNG_EN";
};
eeprom0: qcom,eeprom@0 {
diff --git a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi
index 94166bf8dd3e..11eb288804ff 100644
--- a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi
@@ -40,6 +40,16 @@
vin-supply = <&pm660l_bob>;
};
+ cam_actuator_regulator: cam_actuator_fixed_regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "cam_actuator_regulator";
+ regulator-min-microvolt = <3600000>;
+ regulator-max-microvolt = <3600000>;
+ enable-active-high;
+ gpio = <&tlmm 50 0>;
+ vin-supply = <&pm660l_bob>;
+ };
+
cam_dvdd_gpio_regulator: cam_dvdd_fixed_regulator {
compatible = "regulator-fixed";
regulator-name = "cam_dvdd_gpio_regulator";
@@ -67,14 +77,11 @@
reg = <0x0>;
compatible = "qcom,actuator";
qcom,cci-master = <0>;
- gpios = <&tlmm 50 0>;
- qcom,gpio-vaf = <0>;
- qcom,gpio-req-tbl-num = <0>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_VAF";
- pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_actuator_vaf_active>;
- pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ cam_vaf-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ qcom,cam-vreg-op-mode = <0>;
};
actuator1: qcom,actuator@1 {
@@ -82,14 +89,11 @@
reg = <0x1>;
compatible = "qcom,actuator";
qcom,cci-master = <1>;
- gpios = <&tlmm 50 0>;
- qcom,gpio-vaf = <0>;
- qcom,gpio-req-tbl-num = <0>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_VAF";
- pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_actuator_vaf_active>;
- pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ cam_vaf-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ qcom,cam-vreg-op-mode = <0>;
};
actuator2: qcom,actuator@2 {
@@ -97,14 +101,11 @@
reg = <0x2>;
compatible = "qcom,actuator";
qcom,cci-master = <1>;
- gpios = <&tlmm 50 0>;
- qcom,gpio-vaf = <0>;
- qcom,gpio-req-tbl-num = <0>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_VAF";
- pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_actuator_vaf_active>;
- pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ cam_vaf-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ qcom,cam-vreg-op-mode = <0>;
};
ois0: qcom,ois@0 {
@@ -112,15 +113,31 @@
reg = <0x0>;
compatible = "qcom,ois";
qcom,cci-master = <0>;
- gpios = <&tlmm 50 0>;
- qcom,gpio-vaf = <0>;
+ cam_vaf-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ qcom,cam-vreg-op-mode = <0>;
+ status = "disabled";
+ };
+
+ tof0: qcom,tof@29{
+ cell-index = <0>;
+ reg = <0x29>;
+ compatible = "st,stmvl53l0";
+ qcom,cci-master = <0>;
+ cam_laser-supply = <&cam_actuator_regulator>;
+ qcom,cam-vreg-name = "cam_laser";
+ qcom,cam-vreg-min-voltage = <3600000>;
+ qcom,cam-vreg-max-voltage = <3600000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_tof_active>;
+ pinctrl-1 = <&cam_tof_suspend>;
+ stm,irq-gpio = <&tlmm 45 0x2008>;
+ gpios = <&tlmm 42 0>;
qcom,gpio-req-tbl-num = <0>;
qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_VAF";
- pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_actuator_vaf_active>;
- pinctrl-1 = <&cam_actuator_vaf_suspend>;
- status = "disabled";
+ qcom,gpio-req-tbl-label = "RNG_EN";
};
eeprom0: qcom,eeprom@0 {
diff --git a/arch/arm/boot/dts/qcom/sdm660-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-cdp.dts
index 7b4b68af6188..1b634d7aaf56 100644
--- a/arch/arm/boot/dts/qcom/sdm660-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-cdp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sdm660-cdp", "qcom,sdm660", "qcom,cdp";
qcom,board-id = <1 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
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-headset-jacktype-no-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-headset-jacktype-no-cdp.dts
index 48cfefb4cdd0..76755d99f6df 100644
--- a/arch/arm/boot/dts/qcom/sdm660-headset-jacktype-no-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-headset-jacktype-no-cdp.dts
@@ -22,5 +22,6 @@
compatible = "qcom,sdm660-cdp", "qcom,sdm660", "qcom,cdp";
qcom,board-id = <1 2>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-internal-codec-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-internal-codec-cdp.dts
index 6755385313b1..a7020158c3a5 100644
--- a/arch/arm/boot/dts/qcom/sdm660-internal-codec-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-internal-codec-cdp.dts
@@ -22,5 +22,6 @@
compatible = "qcom,sdm660-cdp", "qcom,sdm660", "qcom,cdp";
qcom,board-id = <1 1>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-internal-codec-mtp.dts b/arch/arm/boot/dts/qcom/sdm660-internal-codec-mtp.dts
index 39da13e7565b..4aeb561e7755 100644
--- a/arch/arm/boot/dts/qcom/sdm660-internal-codec-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-internal-codec-mtp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sdm660-mtp", "qcom,sdm660", "qcom,mtp";
qcom,board-id = <8 1>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&int_codec {
diff --git a/arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-cdp.dts
index caf8af514237..780330c80565 100644
--- a/arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-cdp.dts
@@ -22,5 +22,7 @@
model = "Qualcomm Technologies, Inc. SDM 660 PM660 + PM660A Int. Audio Codec CDP";
compatible = "qcom,sdm660-cdp", "qcom,sdm660", "qcom,cdp";
qcom,board-id = <1 1>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-mtp.dts
index d2ae22879ef4..b9c8c900a973 100644
--- a/arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-internal-codec-pm660a-mtp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDM 660 PM660 + PM660A Int. Audio Codec MTP";
compatible = "qcom,sdm660-mtp", "qcom,sdm660", "qcom,mtp";
qcom,board-id = <8 1>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&int_codec {
diff --git a/arch/arm/boot/dts/qcom/sdm660-mtp.dts b/arch/arm/boot/dts/qcom/sdm660-mtp.dts
index 72bfa20b2bf9..32b294ee6883 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-mtp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sdm660-mtp", "qcom,sdm660", "qcom,mtp";
qcom,board-id = <8 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
index 45b7201fbf71..50f5d83346c6 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
@@ -56,6 +56,16 @@
qcom,master-en = <1>;
status = "okay";
};
+
+ /* GPIO 11 for Home Key */
+ gpio@ca00 {
+ status = "okay";
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,out-strength = <1>;
+ };
};
&i2c_6 { /* BLSP1 QUP6 (NFC) */
diff --git a/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi b/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi
index d902078b1048..e55a67e68b36 100644
--- a/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi
@@ -1101,6 +1101,34 @@
};
};
+ cam_tof_active: cam_tof_active {
+ /* LASER */
+ mux {
+ pins = "gpio50", "gpio42", "gpio45";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio50", "gpio42", "gpio45";
+ bias-pull-up;
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_tof_suspend: cam_tof_suspend {
+ /* LASER */
+ mux {
+ pins = "gpio50", "gpio42", "gpio45";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio50", "gpio42", "gpio45";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
cam_sensor_mclk0_active: cam_sensor_mclk0_active {
/* MCLK0 */
mux {
diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts
index c27f76d3027b..c20318c26373 100644
--- a/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDM 660 PM660 + PM660A CDP";
compatible = "qcom,sdm660-cdp", "qcom,sdm660", "qcom,cdp";
qcom,board-id = <1 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&mdss_dsi {
diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-headset-jacktype-no-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-headset-jacktype-no-cdp.dts
index 281af3b1768e..5af607c0e1cc 100644
--- a/arch/arm/boot/dts/qcom/sdm660-pm660a-headset-jacktype-no-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-headset-jacktype-no-cdp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDM 660 PM660 + PM660A, Headset Jacktype NO, CDP";
compatible = "qcom,sdm660-cdp", "qcom,sdm660", "qcom,cdp";
qcom,board-id = <1 2>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&mdss_dsi0 {
diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts
index eb5e4999fb67..3aaa839e18be 100644
--- a/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts
@@ -22,7 +22,9 @@
model = "Qualcomm Technologies, Inc. SDM 660 PM660 + PM660A MTP";
compatible = "qcom,sdm660-mtp", "qcom,sdm660", "qcom,mtp";
qcom,board-id = <8 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&mdss_dsi {
diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts
index d9d74ea31d3d..848be11dc0a6 100644
--- a/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts
@@ -21,7 +21,9 @@
model = "Qualcomm Technologies, Inc. SDM 660 PM660 + PM660A QRD";
compatible = "qcom,sdm660-qrd", "qcom,sdm660", "qcom,qrd";
qcom,board-id = <0x0012000b 0>;
- qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
};
&pm660a_oledb {
diff --git a/arch/arm/boot/dts/qcom/sdm660-qrd.dts b/arch/arm/boot/dts/qcom/sdm660-qrd.dts
index 4d120e83cb9b..3284e805a093 100644
--- a/arch/arm/boot/dts/qcom/sdm660-qrd.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-qrd.dts
@@ -21,7 +21,8 @@
compatible = "qcom,sdm660-qrd", "qcom,sdm660", "qcom,qrd";
qcom,board-id = <0x1000b 0>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&mdss_mdp {
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-usbc-audio-mtp.dts b/arch/arm/boot/dts/qcom/sdm660-usbc-audio-mtp.dts
index dff55d8e9cf8..b1a752b46a41 100644
--- a/arch/arm/boot/dts/qcom/sdm660-usbc-audio-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-usbc-audio-mtp.dts
@@ -22,7 +22,8 @@
compatible = "qcom,sdm660-mtp", "qcom,sdm660", "qcom,mtp";
qcom,board-id = <8 2>;
qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
- <0x0001001b 0x0201011a 0x0 0x0>;
+ <0x0001001b 0x0201011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>;
};
&tavil_snd {
diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi
index f14c9a32c2f9..c436ce643091 100644
--- a/arch/arm/boot/dts/qcom/sdm660.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660.dtsi
@@ -672,6 +672,16 @@
clock-names = "core", "iface";
};
+ qcom,qbt1000 {
+ compatible = "qcom,qbt1000";
+ clock-names = "core", "iface";
+ clocks = <&clock_gcc GCC_BLSP1_QUP3_SPI_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ clock-frequency = <15000000>;
+ qcom,ipc-gpio = <&tlmm 72 0>;
+ qcom,finger-detect-gpio = <&pm660_gpios 11 0>;
+ };
+
qcom,sensor-information {
compatible = "qcom,sensor-information";
sensor_information0: qcom,sensor-information-0 {
@@ -1244,10 +1254,11 @@
compatible = "qcom,clk-cpu-osm";
reg = <0x179c0000 0x4000>, <0x17916000 0x1000>,
<0x17816000 0x1000>, <0x179d1000 0x1000>,
- <0x00784130 0x8>, <0x17914800 0x800>;
+ <0x00784130 0x8>, <0x00784130 0x8>,
+ <0x17914800 0x800>;
reg-names = "osm", "pwrcl_pll", "perfcl_pll",
- "apcs_common", "perfcl_efuse",
- "pwrcl_acd";
+ "apcs_common", "pwrcl_efuse",
+ "perfcl_efuse", "pwrcl_acd";
qcom,acdtd-val = <0x0000a111 0x0000a111>;
qcom,acdcr-val = <0x002c5ffd 0x002c5ffd>;
@@ -1273,6 +1284,25 @@
< 1747200000 0x0404005b 0x09480048 0x2 7 >,
< 1843200000 0x04040060 0x094c004c 0x3 8 >;
+ qcom,pwrcl-speedbin1-v0 =
+ < 300000000 0x0004000f 0x01200020 0x1 1 >,
+ < 633600000 0x05040021 0x03200020 0x1 2 >,
+ < 902400000 0x0404002f 0x04260026 0x1 3 >,
+ < 1113600000 0x0404003a 0x052e002e 0x2 4 >,
+ < 1401600000 0x04040049 0x073a003a 0x2 5 >,
+ < 1536000000 0x04040050 0x08400040 0x2 6 >,
+ < 1747200000 0x0404005b 0x09480048 0x2 7 >,
+ < 1843200000 0x04040060 0x094c004c 0x3 8 >;
+
+ qcom,pwrcl-speedbin3-v0 =
+ < 300000000 0x0004000f 0x01200020 0x1 1 >,
+ < 633600000 0x05040021 0x03200020 0x1 2 >,
+ < 902400000 0x0404002f 0x04260026 0x1 3 >,
+ < 1113600000 0x0404003a 0x052e002e 0x2 4 >,
+ < 1401600000 0x04040049 0x073a003a 0x2 5 >,
+ < 1536000000 0x04040050 0x08400040 0x2 6 >,
+ < 1612800000 0x04040054 0x09430043 0x2 7 >;
+
qcom,perfcl-speedbin0-v0 =
< 300000000 0x0004000f 0x01200020 0x1 1 >,
< 1113600000 0x0404003a 0x052e002e 0x1 2 >,
@@ -1291,6 +1321,13 @@
< 2150400000 0x04040070 0x0b590059 0x2 6 >,
< 2208000000 0x04040073 0x0b5c005c 0x3 7 >;
+ qcom,perfcl-speedbin3-v0 =
+ < 300000000 0x0004000f 0x01200020 0x1 1 >,
+ < 1113600000 0x0404003a 0x052e002e 0x1 2 >,
+ < 1401600000 0x04040049 0x073a003a 0x2 3 >,
+ < 1747200000 0x0404005b 0x09480048 0x2 4 >,
+ < 1804800000 0x0404005e 0x094b004b 0x2 5 >;
+
qcom,up-timer = <1000 1000>;
qcom,down-timer = <1000 1000>;
qcom,pc-override-index = <0 0>;
@@ -1344,6 +1381,7 @@
< 1113600 >,
< 1401600 >,
< 1536000 >,
+ < 1612800 >,
< 1747200 >,
< 1843200 >;
@@ -1351,6 +1389,7 @@
< 1113600 >,
< 1401600 >,
< 1747200 >,
+ < 1804800 >,
< 1958400 >,
< 2150400 >,
< 2208000 >,
@@ -2045,6 +2084,7 @@
qcom,firmware-name = "modem";
qcom,pil-self-auth;
qcom,sysmon-id = <0>;
+ qcom,minidump-id = <0>;
qcom,ssctl-instance-id = <0x12>;
qcom,qdsp6v62-1-5;
memory-region = <&modem_fw_mem>;
@@ -2119,6 +2159,11 @@
compatible = "qcom,msm-imem-diag-dload";
reg = <0xc8 200>;
};
+
+ ss_mdump@b88 {
+ compatible = "qcom,msm-imem-minidump";
+ reg = <0xb88 28>;
+ };
};
qcom,ghd {
diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-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-ion.dtsi b/arch/arm/boot/dts/qcom/vplatform-lfv-ion.dtsi
index 1176b54835b1..045cc44b2d4c 100644
--- a/arch/arm/boot/dts/qcom/vplatform-lfv-ion.dtsi
+++ b/arch/arm/boot/dts/qcom/vplatform-lfv-ion.dtsi
@@ -22,6 +22,12 @@
qcom,ion-heap-type = "CARVEOUT";
};
+ qcom,ion-heap@27 { /* QSEECOM HEAP */
+ reg = <27>;
+ memory-region = <&qseecom_mem>;
+ qcom,ion-heap-type = "DMA";
+ };
+
qcom,ion-heap@28 { /* Audio Heap */
reg = <28>;
memory-region = <&ion_audio>;
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 b108f7d56e57..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";
@@ -85,6 +112,10 @@
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
<&incall_music_rx>, <&incall_music2_rx>,
+ <&dai_pri_tdm_tx_0>, <&dai_pri_tdm_tx_1>,
+ <&dai_pri_tdm_tx_2>, <&dai_pri_tdm_tx_3>,
+ <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_rx_1>,
+ <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_rx_3>,
<&dai_sec_tdm_tx_0>, <&dai_sec_tdm_tx_1>,
<&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>,
<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
@@ -103,6 +134,10 @@
"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+ "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36867",
+ "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36871",
+ "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36866",
+ "msm-dai-q6-tdm.36868", "msm-dai-q6-tdm.36870",
"msm-dai-q6-tdm.36881", "msm-dai-q6-tdm.36883",
"msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887",
"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
@@ -117,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 {
@@ -292,6 +333,82 @@
};
};
+ qcom,msm-dai-tdm-pri-rx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37120>;
+ qcom,msm-cpudai-tdm-group-num-ports = <4>;
+ qcom,msm-cpudai-tdm-group-port-id = <36864 36866 36868 36870>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <1>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
+ dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36864>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36866>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36868>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36870>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
+ qcom,msm-dai-tdm-pri-tx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37121>;
+ qcom,msm-cpudai-tdm-group-num-ports = <4>;
+ qcom,msm-cpudai-tdm-group-port-id = <36865 36867 36869 36871>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <1>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
+ dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36865>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36867>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36869>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36871>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
qcom,msm-dai-tdm-sec-tx {
compatible = "qcom,msm-dai-tdm";
qcom,msm-cpudai-tdm-group-id = <37137>;
@@ -503,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/s3c2410_defconfig b/arch/arm/configs/s3c2410_defconfig
index f3142369f594..01116ee1284b 100644
--- a/arch/arm/configs/s3c2410_defconfig
+++ b/arch/arm/configs/s3c2410_defconfig
@@ -87,9 +87,9 @@ CONFIG_IPV6_TUNNEL=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_DCCP=m
-CONFIG_NF_CT_PROTO_SCTP=m
-CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig
index 43b6432118f0..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,7 +659,9 @@ 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
CONFIG_SECURITY_SMACK=y
CONFIG_CRYPTO_ECHAINIV=y
diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig
index e3aa35da81ce..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,7 +698,9 @@ 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
CONFIG_SECURITY_SMACK=y
CONFIG_CRYPTO_ECHAINIV=y
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h
index d2315ffd8f12..f13ae153fb24 100644
--- a/arch/arm/include/asm/elf.h
+++ b/arch/arm/include/asm/elf.h
@@ -112,12 +112,8 @@ int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs);
#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE 4096
-/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
- use of this is to invoke "./ld.so someprog" to test out a new version of
- the loader. We need to make sure that it is out of the way of the program
- that it will "exec", and that there is sufficient room for the brk. */
-
-#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2)
+/* This is the base location for PIE (ET_DYN with INTERP) loads. */
+#define ELF_ET_DYN_BASE 0x400000UL
/* When the program starts, a1 contains a pointer to a function to be
registered with atexit, as per the SVR4 ABI. A value of 0 means we
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h
index bfe2a2f5a644..22b73112b75f 100644
--- a/arch/arm/include/asm/ftrace.h
+++ b/arch/arm/include/asm/ftrace.h
@@ -54,6 +54,24 @@ static inline void *return_address(unsigned int level)
#define ftrace_return_address(n) return_address(n)
+#define ARCH_HAS_SYSCALL_MATCH_SYM_NAME
+
+static inline bool arch_syscall_match_sym_name(const char *sym,
+ const char *name)
+{
+ if (!strcmp(sym, "sys_mmap2"))
+ sym = "sys_mmap_pgoff";
+ else if (!strcmp(sym, "sys_statfs64_wrapper"))
+ sym = "sys_statfs64";
+ else if (!strcmp(sym, "sys_fstatfs64_wrapper"))
+ sym = "sys_fstatfs64";
+ else if (!strcmp(sym, "sys_arm_fadvise64_64"))
+ sym = "sys_fadvise64_64";
+
+ /* Ignore case since sym may start with "SyS" instead of "sys" */
+ return !strcasecmp(sym, name);
+}
+
#endif /* ifndef __ASSEMBLY__ */
#endif /* _ASM_ARM_FTRACE */
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index e4a774f7aba1..360cea172b06 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -1636,12 +1636,16 @@ static int kvm_test_age_hva_handler(struct kvm *kvm, gpa_t gpa, void *data)
int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end)
{
+ if (!kvm->arch.pgd)
+ return 0;
trace_kvm_age_hva(start, end);
return handle_hva_to_gpa(kvm, start, end, kvm_age_hva_handler, NULL);
}
int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
{
+ if (!kvm->arch.pgd)
+ return 0;
trace_kvm_test_age_hva(hva);
return handle_hva_to_gpa(kvm, hva, hva, kvm_test_age_hva_handler, NULL);
}
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 0c369a5d59f9..d61f3ae80e15 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -103,6 +103,7 @@ config ARM64
select SYSCTL_EXCEPTION_TRACE
select HAVE_CONTEXT_TRACKING
select HAVE_ARM_SMCCC
+ select THREAD_INFO_IN_TASK
help
ARM 64-bit (AArch64) Linux support.
@@ -247,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/boot/dts/xilinx/zynqmp-ep108.dts b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
index ce5d848251fa..7b34822d61e9 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
@@ -26,7 +26,7 @@
stdout-path = "serial0:115200n8";
};
- memory {
+ memory@0 {
device_type = "memory";
reg = <0x0 0x0 0x40000000>;
};
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
index 857eda5c7217..172402cc1a0f 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
@@ -71,7 +71,7 @@
<1 10 0xf01>;
};
- amba_apu {
+ amba_apu: amba_apu@0 {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <1>;
@@ -191,7 +191,7 @@
};
i2c0: i2c@ff020000 {
- compatible = "cdns,i2c-r1p10";
+ compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10";
status = "disabled";
interrupt-parent = <&gic>;
interrupts = <0 17 4>;
@@ -202,7 +202,7 @@
};
i2c1: i2c@ff030000 {
- compatible = "cdns,i2c-r1p10";
+ compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10";
status = "disabled";
interrupt-parent = <&gic>;
interrupts = <0 18 4>;
diff --git a/arch/arm64/configs/msm-auto-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..7e3bf18b06f7 100644
--- a/arch/arm64/configs/msm-auto-perf_defconfig
+++ b/arch/arm64/configs/msm-auto-perf_defconfig
@@ -220,6 +220,7 @@ CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_SOCKEV_NLMCAST=y
CONFIG_CAN=y
CONFIG_CAN_RH850=y
+CONFIG_CAN_K61=y
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_BTFM_SLIM=y
@@ -261,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
@@ -299,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_HS=y
CONFIG_SERIAL_MSM_SMD=y
CONFIG_DIAG_CHAR=y
@@ -319,6 +319,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
@@ -334,6 +335,7 @@ CONFIG_MSM_BCL_PERIPHERAL_CTL=y
CONFIG_MSM_PM=y
CONFIG_APSS_CORE_EA=y
CONFIG_MSM_APM=y
+CONFIG_SENSORS_GPIO_FAN=y
CONFIG_SENSORS_EPM_ADC=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_LIMITS_MONITOR=y
diff --git a/arch/arm64/configs/msm-auto_defconfig b/arch/arm64/configs/msm-auto_defconfig
index e9ef95772ebd..92fc522c11ed 100644
--- a/arch/arm64/configs/msm-auto_defconfig
+++ b/arch/arm64/configs/msm-auto_defconfig
@@ -222,6 +222,7 @@ CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_SOCKEV_NLMCAST=y
CONFIG_CAN=y
CONFIG_CAN_RH850=y
+CONFIG_CAN_K61=y
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_BTFM_SLIM=y
@@ -262,6 +263,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 +302,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 +322,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
@@ -337,6 +338,7 @@ CONFIG_MSM_BCL_PERIPHERAL_CTL=y
CONFIG_MSM_PM=y
CONFIG_APSS_CORE_EA=y
CONFIG_MSM_APM=y
+CONFIG_SENSORS_GPIO_FAN=y
CONFIG_SENSORS_EPM_ADC=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_LIMITS_MONITOR=y
diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig
index acde18d2fe31..61418724b897 100644
--- a/arch/arm64/configs/msm-perf_defconfig
+++ b/arch/arm64/configs/msm-perf_defconfig
@@ -34,7 +34,7 @@ CONFIG_EMBEDDED=y
# CONFIG_SLUB_DEBUG is not set
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
-CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig
index f510f43427ce..ee2b9fa628ff 100644
--- a/arch/arm64/configs/msm_defconfig
+++ b/arch/arm64/configs/msm_defconfig
@@ -31,7 +31,7 @@ CONFIG_KALLSYMS_ALL=y
CONFIG_EMBEDDED=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
-CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 894cb466b075..e16fc58ce913 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -40,7 +40,7 @@ CONFIG_EMBEDDED=y
# CONFIG_SLUB_DEBUG is not set
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
-CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
@@ -295,6 +295,7 @@ CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_ATH_CARDS=y
CONFIG_WIL6210=m
CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS_GENL=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_KEYRESET=y
CONFIG_KEYBOARD_GPIO=y
@@ -320,6 +321,7 @@ CONFIG_SERIAL_MSM_SMD=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
CONFIG_MSM_ADSPRPC=y
CONFIG_MSM_RDBG=m
CONFIG_I2C_CHARDEV=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index 993e350ef000..3cbcc10ceb1d 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -38,7 +38,7 @@ CONFIG_KALLSYMS_ALL=y
CONFIG_EMBEDDED=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
-CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
@@ -296,6 +296,7 @@ CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_ATH_CARDS=y
CONFIG_WIL6210=m
CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS_GENL=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_KEYRESET=y
CONFIG_KEYBOARD_GPIO=y
@@ -324,6 +325,7 @@ CONFIG_SERIAL_MSM_SMD=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
CONFIG_MSM_ADSPRPC=y
CONFIG_MSM_RDBG=m
CONFIG_I2C_CHARDEV=y
@@ -696,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 b6cb1a4b8574..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
@@ -38,7 +41,7 @@ CONFIG_EMBEDDED=y
# CONFIG_SLUB_DEBUG is not set
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
-CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
@@ -69,6 +72,7 @@ CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
+CONFIG_ARM64_SW_TTBR0_PAN=y
CONFIG_RANDOMIZE_BASE=y
# CONFIG_EFI is not set
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
@@ -92,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
@@ -241,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
@@ -299,6 +305,7 @@ CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_STMVL53L0=y
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
@@ -309,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
@@ -557,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
@@ -574,6 +583,7 @@ CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
+CONFIG_MSM_QBT1000=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
@@ -607,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
@@ -639,7 +652,9 @@ 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
CONFIG_SECURITY_SMACK=y
CONFIG_CRYPTO_ECHAINIV=y
diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig
index ccef87ff6a04..7819916fb841 100644
--- a/arch/arm64/configs/sdm660_defconfig
+++ b/arch/arm64/configs/sdm660_defconfig
@@ -40,7 +40,7 @@ CONFIG_KALLSYMS_ALL=y
CONFIG_EMBEDDED=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
-CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
@@ -73,6 +73,7 @@ CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
+CONFIG_ARM64_SW_TTBR0_PAN=y
CONFIG_RANDOMIZE_BASE=y
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
@@ -96,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
@@ -308,6 +310,7 @@ CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_KEYCHORD=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=y
+CONFIG_INPUT_STMVL53L0=y
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
@@ -318,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
@@ -577,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
@@ -599,6 +604,7 @@ CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
+CONFIG_MSM_QBT1000=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
@@ -633,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
@@ -690,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
@@ -712,7 +722,9 @@ 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
CONFIG_SECURITY_SMACK=y
CONFIG_CRYPTO_ECHAINIV=y
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 02e4b7e4bdbf..0e67a507dfbc 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -235,14 +235,25 @@ lr .req x30 // link register
.endm
/*
+ * @dst: Result of per_cpu(sym, smp_processor_id())
* @sym: The name of the per-cpu variable
- * @reg: Result of per_cpu(sym, smp_processor_id())
* @tmp: scratch register
*/
- .macro this_cpu_ptr, sym, reg, tmp
- adr_l \reg, \sym
+ .macro adr_this_cpu, dst, sym, tmp
+ adr_l \dst, \sym
mrs \tmp, tpidr_el1
- add \reg, \reg, \tmp
+ add \dst, \dst, \tmp
+ .endm
+
+ /*
+ * @dst: Result of READ_ONCE(per_cpu(sym, smp_processor_id()))
+ * @sym: The name of the per-cpu variable
+ * @tmp: scratch register
+ */
+ .macro ldr_this_cpu dst, sym, tmp
+ adr_l \dst, \sym
+ mrs \tmp, tpidr_el1
+ ldr \dst, [\dst, \tmp]
.endm
/*
diff --git a/arch/arm64/include/asm/current.h b/arch/arm64/include/asm/current.h
new file mode 100644
index 000000000000..483a6c9d3e10
--- /dev/null
+++ b/arch/arm64/include/asm/current.h
@@ -0,0 +1,35 @@
+#ifndef __ASM_CURRENT_H
+#define __ASM_CURRENT_H
+
+#include <linux/compiler.h>
+
+#include <asm/sysreg.h>
+
+#ifndef __ASSEMBLY__
+
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+struct task_struct;
+
+/*
+ * We don't use read_sysreg() as we want the compiler to cache the value where
+ * possible.
+ */
+static __always_inline struct task_struct *get_current(void)
+{
+ unsigned long sp_el0;
+
+ asm ("mrs %0, sp_el0" : "=r" (sp_el0));
+
+ return (struct task_struct *)sp_el0;
+}
+#define current get_current()
+#else
+#include <linux/thread_info.h>
+#define get_current() (current_thread_info()->task)
+#define current get_current()
+#endif
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_CURRENT_H */
+
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index a383c288ef49..9d9287277201 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -114,12 +114,11 @@
#define ELF_EXEC_PAGESIZE PAGE_SIZE
/*
- * This is the location that an ET_DYN program is loaded if exec'ed. Typical
- * use of this is to invoke "./ld.so someprog" to test out a new version of
- * the loader. We need to make sure that it is out of the way of the program
- * that it will "exec", and that there is sufficient room for the brk.
+ * This is the base location for PIE (ET_DYN with INTERP) loads. On
+ * 64-bit, this is above 4GB to leave the entire 32-bit address
+ * space open for things that want to use the area for 32-bit pointers.
*/
-#define ELF_ET_DYN_BASE (2 * TASK_SIZE_64 / 3)
+#define ELF_ET_DYN_BASE (2 * TASK_SIZE_64 / 3)
#ifndef __ASSEMBLY__
@@ -170,7 +169,8 @@ extern int arch_setup_additional_pages(struct linux_binprm *bprm,
#ifdef CONFIG_COMPAT
-#define COMPAT_ELF_ET_DYN_BASE (2 * TASK_SIZE_32 / 3)
+/* PIE load location for compat arm. Must match ARM ELF_ET_DYN_BASE. */
+#define COMPAT_ELF_ET_DYN_BASE 0x000400000UL
/* AArch32 registers. */
#define COMPAT_ELF_NGREG 18
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index eea245bf546a..3112c2a9d96f 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -167,9 +167,9 @@ static inline u64 __raw_readq_no_log(const volatile void __iomem *addr)
#define readq_relaxed_no_log(c) ({ u64 __v = le64_to_cpu((__force __le64)__raw_readq_no_log(c)); __v; })
#define writeb_relaxed_no_log(v, c) ((void)__raw_writeb_no_log((v), (c)))
-#define writew_relaxed_no_log(v, c) ((void)__raw_writew_no_log((__force u16)cpu_to_le32(v), (c)))
+#define writew_relaxed_no_log(v, c) ((void)__raw_writew_no_log((__force u16)cpu_to_le16(v), (c)))
#define writel_relaxed_no_log(v, c) ((void)__raw_writel_no_log((__force u32)cpu_to_le32(v), (c)))
-#define writeq_relaxed_no_log(v, c) ((void)__raw_writeq_no_log((__force u64)cpu_to_le32(v), (c)))
+#define writeq_relaxed_no_log(v, c) ((void)__raw_writeq_no_log((__force u64)cpu_to_le64(v), (c)))
/*
* I/O memory access primitives. Reads are ordered relative to any
diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h
index 8a336852eeba..2ce1a0262a59 100644
--- a/arch/arm64/include/asm/percpu.h
+++ b/arch/arm64/include/asm/percpu.h
@@ -16,6 +16,8 @@
#ifndef __ASM_PERCPU_H
#define __ASM_PERCPU_H
+#include <asm/stack_pointer.h>
+
static inline void set_my_cpu_offset(unsigned long off)
{
asm volatile("msr tpidr_el1, %0" :: "r" (off) : "memory");
diff --git a/arch/arm64/include/asm/perf_event.h b/arch/arm64/include/asm/perf_event.h
index da4397e14e0d..0c38c189fb3b 100644
--- a/arch/arm64/include/asm/perf_event.h
+++ b/arch/arm64/include/asm/perf_event.h
@@ -17,6 +17,8 @@
#ifndef __ASM_PERF_EVENT_H
#define __ASM_PERF_EVENT_H
+#include <asm/stack_pointer.h>
+
#ifdef CONFIG_PERF_EVENTS
struct pt_regs;
extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index 2013a4dc5124..4325b3622a92 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -16,11 +16,22 @@
#ifndef __ASM_SMP_H
#define __ASM_SMP_H
+#include <asm/percpu.h>
+
#include <linux/threads.h>
#include <linux/cpumask.h>
#include <linux/thread_info.h>
-#define raw_smp_processor_id() (current_thread_info()->cpu)
+DECLARE_PER_CPU_READ_MOSTLY(int, cpu_number);
+
+/*
+ * We don't use this_cpu_read(cpu_number) as that has implicit writes to
+ * preempt_count, and associated (compiler) barriers, that we'd like to avoid
+ * the expense of. If we're preemptible, the value can be stale at use anyway.
+ * And we can't use this_cpu_ptr() either, as that winds up recursing back
+ * here under CONFIG_DEBUG_PREEMPT=y.
+ */
+#define raw_smp_processor_id() (*raw_cpu_ptr(&cpu_number))
struct seq_file;
@@ -57,6 +68,9 @@ asmlinkage void secondary_start_kernel(void);
*/
struct secondary_data {
void *stack;
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ struct task_struct *task;
+#endif
};
extern struct secondary_data secondary_data;
extern void secondary_entry(void);
diff --git a/arch/arm64/include/asm/stack_pointer.h b/arch/arm64/include/asm/stack_pointer.h
new file mode 100644
index 000000000000..ffcdf742cddf
--- /dev/null
+++ b/arch/arm64/include/asm/stack_pointer.h
@@ -0,0 +1,9 @@
+#ifndef __ASM_STACK_POINTER_H
+#define __ASM_STACK_POINTER_H
+
+/*
+ * how to get the current stack pointer from C
+ */
+register unsigned long current_stack_pointer asm ("sp");
+
+#endif /* __ASM_STACK_POINTER_H */
diff --git a/arch/arm64/include/asm/suspend.h b/arch/arm64/include/asm/suspend.h
index 024d623f662e..92d6a628e478 100644
--- a/arch/arm64/include/asm/suspend.h
+++ b/arch/arm64/include/asm/suspend.h
@@ -1,7 +1,7 @@
#ifndef __ASM_SUSPEND_H
#define __ASM_SUSPEND_H
-#define NR_CTX_REGS 10
+#define NR_CTX_REGS 12
#define NR_CALLEE_SAVED_REGS 12
/*
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 4bb038ec6453..db6d058ab0f3 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -36,25 +36,36 @@
struct task_struct;
+#include <asm/stack_pointer.h>
#include <asm/types.h>
typedef unsigned long mm_segment_t;
/*
* low level task data that entry.S needs immediate access to.
- * __switch_to() assumes cpu_context follows immediately after cpu_domain.
*/
struct thread_info {
unsigned long flags; /* low level flags */
mm_segment_t addr_limit; /* address limit */
+#ifndef CONFIG_THREAD_INFO_IN_TASK
struct task_struct *task; /* main task structure */
+#endif
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
u64 ttbr0; /* saved TTBR0_EL1 */
#endif
int preempt_count; /* 0 => preemptable, <0 => bug */
+#ifndef CONFIG_THREAD_INFO_IN_TASK
int cpu; /* cpu */
+#endif
};
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+#define INIT_THREAD_INFO(tsk) \
+{ \
+ .preempt_count = INIT_PREEMPT_COUNT, \
+ .addr_limit = KERNEL_DS, \
+}
+#else
#define INIT_THREAD_INFO(tsk) \
{ \
.task = &tsk, \
@@ -63,14 +74,6 @@ struct thread_info {
.addr_limit = KERNEL_DS, \
}
-#define init_thread_info (init_thread_union.thread_info)
-#define init_stack (init_thread_union.stack)
-
-/*
- * how to get the current stack pointer from C
- */
-register unsigned long current_stack_pointer asm ("sp");
-
/*
* how to get the thread information struct from C
*/
@@ -88,6 +91,11 @@ static inline struct thread_info *current_thread_info(void)
return (struct thread_info *)sp_el0;
}
+#define init_thread_info (init_thread_union.thread_info)
+#endif
+
+#define init_stack (init_thread_union.stack)
+
#define thread_saved_pc(tsk) \
((unsigned long)(tsk->thread.cpu_context.pc))
#define thread_saved_sp(tsk) \
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 350c0e99fc6b..36c4307c4af3 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -35,11 +35,16 @@ int main(void)
{
DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
BLANK();
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ DEFINE(TSK_TI_FLAGS, offsetof(struct task_struct, thread_info.flags));
+ DEFINE(TSK_TI_PREEMPT, offsetof(struct task_struct, thread_info.preempt_count));
+ DEFINE(TSK_TI_ADDR_LIMIT, offsetof(struct task_struct, thread_info.addr_limit));
+ DEFINE(TSK_STACK, offsetof(struct task_struct, stack));
+#else
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
- DEFINE(TI_TASK, offsetof(struct thread_info, task));
- DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
+#endif
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
DEFINE(TSK_TI_TTBR0, offsetof(struct thread_info, ttbr0));
#endif
@@ -124,6 +129,11 @@ int main(void)
DEFINE(TZ_MINWEST, offsetof(struct timezone, tz_minuteswest));
DEFINE(TZ_DSTTIME, offsetof(struct timezone, tz_dsttime));
BLANK();
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ DEFINE(CPU_BOOT_STACK, offsetof(struct secondary_data, stack));
+ DEFINE(CPU_BOOT_TASK, offsetof(struct secondary_data, task));
+ BLANK();
+#endif
#ifdef CONFIG_KVM_ARM_HOST
DEFINE(VCPU_CONTEXT, offsetof(struct kvm_vcpu, arch.ctxt));
DEFINE(CPU_GP_REGS, offsetof(struct kvm_cpu_context, gp_regs));
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 7822e36d87fb..381f9febcdb6 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -93,9 +93,14 @@
.if \el == 0
mrs x21, sp_el0
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ ldr_this_cpu tsk, __entry_task, x20 // Ensure MDSCR_EL1.SS is clear,
+ ldr x19, [tsk, #TSK_TI_FLAGS] // since we can unmask debug
+#else
mov tsk, sp
and tsk, tsk, #~(THREAD_SIZE - 1) // Ensure MDSCR_EL1.SS is clear,
ldr x19, [tsk, #TI_FLAGS] // since we can unmask debug
+#endif
disable_step_tsk x19, x20 // exceptions when scheduling.
mov x29, xzr // fp pointed to user-space
@@ -103,10 +108,18 @@
add x21, sp, #S_FRAME_SIZE
get_thread_info tsk
/* Save the task's original addr_limit and set USER_DS (TASK_SIZE_64) */
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ ldr x20, [tsk, #TSK_TI_ADDR_LIMIT]
+#else
ldr x20, [tsk, #TI_ADDR_LIMIT]
+#endif
str x20, [sp, #S_ORIG_ADDR_LIMIT]
mov x20, #TASK_SIZE_64
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ str x20, [tsk, #TSK_TI_ADDR_LIMIT]
+#else
str x20, [tsk, #TI_ADDR_LIMIT]
+#endif
ALTERNATIVE(nop, SET_PSTATE_UAO(0), ARM64_HAS_UAO, CONFIG_ARM64_UAO)
.endif /* \el == 0 */
mrs x22, elr_el1
@@ -168,7 +181,11 @@ alternative_else_nop_endif
.if \el != 0
/* Restore the task's original addr_limit. */
ldr x20, [sp, #S_ORIG_ADDR_LIMIT]
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ str x20, [tsk, #TSK_TI_ADDR_LIMIT]
+#else
str x20, [tsk, #TI_ADDR_LIMIT]
+#endif
/* No need to restore UAO, it will be restored from SPSR_EL1 */
.endif
@@ -258,15 +275,22 @@ alternative_endif
mov x19, sp // preserve the original sp
/*
- * Compare sp with the current thread_info, if the top
- * ~(THREAD_SIZE - 1) bits match, we are on a task stack, and
- * should switch to the irq stack.
+ * Compare sp with the base of the task stack.
+ * If the top ~(THREAD_SIZE - 1) bits match, we are on a task stack,
+ * and should switch to the irq stack.
*/
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ ldr x25, [tsk, TSK_STACK]
+ eor x25, x25, x19
+ and x25, x25, #~(THREAD_SIZE - 1)
+ cbnz x25, 9998f
+#else
and x25, x19, #~(THREAD_SIZE - 1)
cmp x25, tsk
b.ne 9998f
+#endif
- this_cpu_ptr irq_stack, x25, x26
+ adr_this_cpu x25, irq_stack, x26
mov x26, #IRQ_STACK_START_SP
add x26, x25, x26
@@ -498,9 +522,17 @@ el1_irq:
irq_handler
#ifdef CONFIG_PREEMPT
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ ldr w24, [tsk, #TSK_TI_PREEMPT] // get preempt count
+#else
ldr w24, [tsk, #TI_PREEMPT] // get preempt count
+#endif
cbnz w24, 1f // preempt count != 0
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ ldr x0, [tsk, #TSK_TI_FLAGS] // get flags
+#else
ldr x0, [tsk, #TI_FLAGS] // get flags
+#endif
tbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling?
bl el1_preempt
1:
@@ -515,7 +547,11 @@ ENDPROC(el1_irq)
el1_preempt:
mov x24, lr
1: bl preempt_schedule_irq // irq en/disable is done inside
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ ldr x0, [tsk, #TSK_TI_FLAGS] // get new tasks TI_FLAGS
+#else
ldr x0, [tsk, #TI_FLAGS] // get new tasks TI_FLAGS
+#endif
tbnz x0, #TIF_NEED_RESCHED, 1b // needs rescheduling?
ret x24
#endif
@@ -773,8 +809,12 @@ ENTRY(cpu_switch_to)
mov v15.16b, v15.16b
#endif
mov sp, x9
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ msr sp_el0, x1
+#else
and x9, x9, #~(THREAD_SIZE - 1)
msr sp_el0, x9
+#endif
ret
ENDPROC(cpu_switch_to)
@@ -785,7 +825,11 @@ ENDPROC(cpu_switch_to)
ret_fast_syscall:
disable_irq // disable interrupts
str x0, [sp, #S_X0] // returned x0
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for syscall tracing
+#else
ldr x1, [tsk, #TI_FLAGS] // re-check for syscall tracing
+#endif
and x2, x1, #_TIF_SYSCALL_WORK
cbnz x2, ret_fast_syscall_trace
and x2, x1, #_TIF_WORK_MASK
@@ -817,7 +861,11 @@ work_resched:
*/
ret_to_user:
disable_irq // disable interrupts
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ ldr x1, [tsk, #TSK_TI_FLAGS]
+#else
ldr x1, [tsk, #TI_FLAGS]
+#endif
and x2, x1, #_TIF_WORK_MASK
cbnz x2, work_pending
enable_step_tsk x1, x2
@@ -849,7 +897,11 @@ el0_svc_naked: // compat entry point
enable_dbg_and_irq
ct_user_exit 1
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ ldr x16, [tsk, #TSK_TI_FLAGS] // check for syscall hooks
+#else
ldr x16, [tsk, #TI_FLAGS] // check for syscall hooks
+#endif
tst x16, #_TIF_SYSCALL_WORK
b.ne __sys_trace
cmp scno, sc_nr // check upper syscall limit
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index a1c2ac38771d..0a0cd0476665 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -418,6 +418,7 @@ ENDPROC(__create_page_tables)
.set initial_sp, init_thread_union + THREAD_START_SP
__primary_switched:
mov x28, lr // preserve LR
+
adr_l x8, vectors // load VBAR_EL1 with virtual
msr vbar_el1, x8 // vector table address
isb
@@ -430,10 +431,18 @@ __primary_switched:
bl __pi_memset
dsb ishst // Make zero page visible to PTW
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ adrp x4, init_thread_union
+ add sp, x4, #THREAD_SIZE
+ adr_l x5, init_task
+ msr sp_el0, x5 // Save thread_info
+#else
adr_l sp, initial_sp, x4
mov x4, sp
and x4, x4, #~(THREAD_SIZE - 1)
msr sp_el0, x4 // Save thread_info
+#endif
+
str_l x21, __fdt_pointer, x5 // Save FDT pointer
ldr_l x4, kimage_vaddr // Save the offset between
@@ -642,11 +651,18 @@ __secondary_switched:
adr_l x5, vectors
msr vbar_el1, x5
isb
-
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ adr_l x0, secondary_data
+ ldr x1, [x0, #CPU_BOOT_STACK] // get secondary_data.stack
+ mov sp, x1
+ ldr x2, [x0, #CPU_BOOT_TASK]
+ msr sp_el0, x2
+#else
ldr_l x0, secondary_data // get secondary_data.stack
mov sp, x0
and x0, x0, #~(THREAD_SIZE - 1)
msr sp_el0, x0 // save thread_info
+#endif
mov x29, #0
b secondary_start_kernel
ENDPROC(__secondary_switched)
diff --git a/arch/arm64/kernel/perf_trace_counters.c b/arch/arm64/kernel/perf_trace_counters.c
index dc92b29ac103..7b852e36eaa2 100644
--- a/arch/arm64/kernel/perf_trace_counters.c
+++ b/arch/arm64/kernel/perf_trace_counters.c
@@ -65,7 +65,7 @@ void tracectr_notifier(void *ignore, bool preempt,
{
u32 cnten_val;
int current_pid;
- u32 cpu = task_thread_info(next)->cpu;
+ u32 cpu = task_cpu(next);
if (tp_pid_state != 1)
return;
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index fc0a7aa2ca82..9918489f5af3 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -45,6 +45,9 @@
#include <linux/personality.h>
#include <linux/notifier.h>
#include <trace/events/power.h>
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+#include <linux/percpu.h>
+#endif
#include <asm/alternative.h>
#include <asm/compat.h>
@@ -394,6 +397,22 @@ void uao_thread_switch(struct task_struct *next)
}
}
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+/*
+ * We store our current task in sp_el0, which is clobbered by userspace. Keep a
+ * shadow copy so that we can restore this upon entry from userspace.
+ *
+ * This is *only* for exception entry from EL0, and is not valid until we
+ * __switch_to() a user task.
+ */
+DEFINE_PER_CPU(struct task_struct *, __entry_task);
+
+static void entry_task_switch(struct task_struct *next)
+{
+ __this_cpu_write(__entry_task, next);
+}
+#endif
+
/*
* Thread switching.
*/
@@ -406,6 +425,9 @@ struct task_struct *__switch_to(struct task_struct *prev,
tls_thread_switch(next);
hw_breakpoint_thread_switch(next);
contextidr_thread_switch(next);
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ entry_task_switch(next);
+#endif
uao_thread_switch(next);
/*
@@ -423,27 +445,35 @@ struct task_struct *__switch_to(struct task_struct *prev,
unsigned long get_wchan(struct task_struct *p)
{
struct stackframe frame;
- unsigned long stack_page;
+ unsigned long stack_page, ret = 0;
int count = 0;
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
+ stack_page = (unsigned long)try_get_task_stack(p);
+ if (!stack_page)
+ return 0;
+
frame.fp = thread_saved_fp(p);
frame.sp = thread_saved_sp(p);
frame.pc = thread_saved_pc(p);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
frame.graph = p->curr_ret_stack;
#endif
- stack_page = (unsigned long)task_stack_page(p);
do {
if (frame.sp < stack_page ||
frame.sp >= stack_page + THREAD_SIZE ||
unwind_frame(p, &frame))
- return 0;
- if (!in_sched_functions(frame.pc))
- return frame.pc;
+ goto out;
+ if (!in_sched_functions(frame.pc)) {
+ ret = frame.pc;
+ goto out;
+ }
} while (count ++ < 16);
- return 0;
+
+out:
+ put_task_stack(p);
+ return ret;
}
unsigned long arch_align_stack(unsigned long sp)
diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c
index 1718706fde83..12a87f2600f2 100644
--- a/arch/arm64/kernel/return_address.c
+++ b/arch/arm64/kernel/return_address.c
@@ -12,6 +12,7 @@
#include <linux/export.h>
#include <linux/ftrace.h>
+#include <asm/stack_pointer.h>
#include <asm/stacktrace.h>
struct return_address_data {
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 30af178c640c..b8b40d95ebef 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -362,12 +362,16 @@ void __init setup_arch(char **cmdline_p)
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
/*
- * Make sure init_thread_info.ttbr0 always generates translation
+ * Make sure thread_info.ttbr0 always generates translation
* faults in case uaccess_enable() is inadvertently called by the init
* thread.
*/
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ init_task.thread_info.ttbr0 = virt_to_phys(empty_zero_page);
+#else
init_thread_info.ttbr0 = virt_to_phys(empty_zero_page);
#endif
+#endif
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S
index 9a3aec97ac09..9e3cb29e4c50 100644
--- a/arch/arm64/kernel/sleep.S
+++ b/arch/arm64/kernel/sleep.S
@@ -124,9 +124,6 @@ ENTRY(_cpu_resume)
/* load sp from context */
ldr x2, [x0, #CPU_CTX_SP]
mov sp, x2
- /* save thread_info */
- and x2, x2, #~(THREAD_SIZE - 1)
- msr sp_el0, x2
/*
* cpu_do_resume expects x0 to contain context address pointer
*/
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index a3a6b2ea9b4d..961fd5d2e7ce 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -54,10 +54,14 @@
#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>
+DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number);
+EXPORT_PER_CPU_SYMBOL(cpu_number);
+
/*
* as from 2.5, kernels no longer have an init_tasks structure
* so we need some other way of telling a new secondary core
@@ -97,6 +101,9 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
* We need to tell the secondary core where to find its stack and the
* page tables.
*/
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ secondary_data.task = idle;
+#endif
secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;
__flush_dcache_area(&secondary_data, sizeof(secondary_data));
@@ -120,6 +127,9 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
pr_err("CPU%u: failed to boot: %d\n", cpu, ret);
}
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ secondary_data.task = NULL;
+#endif
secondary_data.stack = NULL;
return ret;
@@ -137,7 +147,12 @@ static void smp_store_cpu_info(unsigned int cpuid)
asmlinkage void secondary_start_kernel(void)
{
struct mm_struct *mm = &init_mm;
- unsigned int cpu = smp_processor_id();
+ unsigned int cpu;
+
+ cpu = task_cpu(current);
+ set_my_cpu_offset(per_cpu_offset(cpu));
+
+ pr_debug("CPU%u: Booted secondary processor\n", cpu);
/*
* All kernel threads share the same mm context; grab a
@@ -146,10 +161,6 @@ asmlinkage void secondary_start_kernel(void)
atomic_inc(&mm->mm_count);
current->active_mm = mm;
- set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
-
- pr_debug("CPU%u: Booted secondary processor\n", cpu);
-
/*
* TTBR0 is only used for the identity mapping at this stage. Make it
* point to zero page to avoid speculatively fetching new entries.
@@ -632,6 +643,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
if (max_cpus == 0)
break;
+ per_cpu(cpu_number, cpu) = cpu;
+
if (cpu == smp_processor_id())
continue;
@@ -740,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/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
index 1fd1a9a6596f..ea3e453fdd14 100644
--- a/arch/arm64/kernel/stacktrace.c
+++ b/arch/arm64/kernel/stacktrace.c
@@ -23,6 +23,7 @@
#include <linux/stacktrace.h>
#include <asm/irq.h>
+#include <asm/stack_pointer.h>
#include <asm/stacktrace.h>
/*
@@ -130,7 +131,6 @@ void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
break;
}
}
-EXPORT_SYMBOL(walk_stackframe);
#ifdef CONFIG_STACKTRACE
struct stack_trace_data {
@@ -162,6 +162,9 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
struct stack_trace_data data;
struct stackframe frame;
+ if (!try_get_task_stack(tsk))
+ return;
+
data.trace = trace;
data.skip = trace->skip;
@@ -183,6 +186,8 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
walk_stackframe(tsk, &frame, save_trace, &data);
if (trace->nr_entries < trace->max_entries)
trace->entries[trace->nr_entries++] = ULONG_MAX;
+
+ put_task_stack(tsk);
}
EXPORT_SYMBOL(save_stack_trace_tsk);
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c
index 0acdb63d19b6..468b939f3471 100644
--- a/arch/arm64/kernel/suspend.c
+++ b/arch/arm64/kernel/suspend.c
@@ -45,12 +45,6 @@ void notrace __cpu_suspend_exit(void)
cpu_uninstall_idmap();
/*
- * Restore per-cpu offset before any kernel
- * subsystem relying on it has a chance to run.
- */
- set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
-
- /*
* Restore HW breakpoint registers to sane values
* before debug exceptions are possibly reenabled
* through local_dbg_restore.
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index f4ab8bc661da..ea40dc101433 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -38,6 +38,7 @@
#include <asm/esr.h>
#include <asm/insn.h>
#include <asm/traps.h>
+#include <asm/stack_pointer.h>
#include <asm/stacktrace.h>
#include <asm/exception.h>
#include <asm/system_misc.h>
@@ -153,6 +154,14 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
unsigned long irq_stack_ptr;
int skip;
+ pr_debug("%s(regs = %pK tsk = %pK)\n", __func__, regs, tsk);
+
+ if (!tsk)
+ tsk = current;
+
+ if (!try_get_task_stack(tsk))
+ return;
+
/*
* Switching between stacks is valid when tracing current and in
* non-preemptible context.
@@ -223,6 +232,8 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
stack + sizeof(struct pt_regs), false);
}
}
+
+ put_task_stack(tsk);
}
void show_stack(struct task_struct *tsk, unsigned long *sp)
@@ -238,10 +249,9 @@ void show_stack(struct task_struct *tsk, unsigned long *sp)
#endif
#define S_SMP " SMP"
-static int __die(const char *str, int err, struct thread_info *thread,
- struct pt_regs *regs)
+static int __die(const char *str, int err, struct pt_regs *regs)
{
- struct task_struct *tsk = thread->task;
+ struct task_struct *tsk = current;
static int die_counter;
int ret;
@@ -256,7 +266,8 @@ static int __die(const char *str, int err, struct thread_info *thread,
print_modules();
__show_regs(regs);
pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",
- TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
+ TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk),
+ end_of_stack(tsk));
if (!user_mode(regs) || in_interrupt()) {
dump_backtrace(regs, tsk);
@@ -321,7 +332,6 @@ static void oops_end(unsigned long flags, struct pt_regs *regs, int notify)
*/
void die(const char *str, struct pt_regs *regs, int err)
{
- struct thread_info *thread = current_thread_info();
enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE;
unsigned long flags = oops_begin();
int ret;
@@ -331,7 +341,7 @@ void die(const char *str, struct pt_regs *regs, int err)
if (bug_type != BUG_TRAP_TYPE_NONE)
str = "Oops - BUG";
- ret = __die(str, err, thread, regs);
+ ret = __die(str, err, regs);
oops_end(flags, regs, ret);
}
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/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 1804aea44faa..2720d47da366 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -92,21 +92,21 @@ void show_pte(struct mm_struct *mm, unsigned long addr)
break;
pud = pud_offset(pgd, addr);
- printk(", *pud=%016llx", pud_val(*pud));
+ pr_cont(", *pud=%016llx", pud_val(*pud));
if (pud_none(*pud) || pud_bad(*pud))
break;
pmd = pmd_offset(pud, addr);
- printk(", *pmd=%016llx", pmd_val(*pmd));
+ pr_cont(", *pmd=%016llx", pmd_val(*pmd));
if (pmd_none(*pmd) || pmd_bad(*pmd))
break;
pte = pte_offset_map(pmd, addr);
- printk(", *pte=%016llx", pte_val(*pte));
+ pr_cont(", *pte=%016llx", pte_val(*pte));
pte_unmap(pte);
} while(0);
- printk("\n");
+ pr_cont("\n");
}
#ifdef CONFIG_ARM64_HW_AFDBM
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 81a0de4e457d..d780180106c1 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -116,11 +116,14 @@ ENTRY(cpu_do_suspend)
mrs x8, mdscr_el1
mrs x9, oslsr_el1
mrs x10, sctlr_el1
+ mrs x11, tpidr_el1
+ mrs x12, sp_el0
stp x2, x3, [x0]
stp x4, xzr, [x0, #16]
stp x5, x6, [x0, #32]
stp x7, x8, [x0, #48]
stp x9, x10, [x0, #64]
+ stp x11, x12, [x0, #80]
ret
ENDPROC(cpu_do_suspend)
@@ -135,6 +138,7 @@ ENTRY(cpu_do_resume)
ldp x6, x8, [x0, #32]
ldp x9, x10, [x0, #48]
ldp x11, x12, [x0, #64]
+ ldp x13, x14, [x0, #80]
msr tpidr_el0, x2
msr tpidrro_el0, x3
msr contextidr_el1, x4
@@ -148,6 +152,8 @@ ENTRY(cpu_do_resume)
msr vbar_el1, x9
msr mdscr_el1, x10
msr sctlr_el1, x12
+ msr tpidr_el1, x13
+ msr sp_el0, x14
/*
* Restore oslsr_el1 by writing oslar_el1
*/
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index 2c86a4ef6742..7091a367eeda 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -45,7 +45,7 @@ config IA64
select GENERIC_SMP_IDLE_THREAD
select ARCH_INIT_TASK
select ARCH_TASK_STRUCT_ALLOCATOR
- select ARCH_THREAD_INFO_ALLOCATOR
+ select ARCH_THREAD_STACK_ALLOCATOR
select ARCH_CLOCKSOURCE_DATA
select GENERIC_TIME_VSYSCALL_OLD
select SYSCTL_ARCH_UNALIGN_NO_WARN
diff --git a/arch/ia64/include/asm/thread_info.h b/arch/ia64/include/asm/thread_info.h
index aa995b67c3f5..d1212b84fb83 100644
--- a/arch/ia64/include/asm/thread_info.h
+++ b/arch/ia64/include/asm/thread_info.h
@@ -48,15 +48,15 @@ struct thread_info {
#ifndef ASM_OFFSETS_C
/* how to get the thread information struct from C */
#define current_thread_info() ((struct thread_info *) ((char *) current + IA64_TASK_SIZE))
-#define alloc_thread_info_node(tsk, node) \
- ((struct thread_info *) ((char *) (tsk) + IA64_TASK_SIZE))
+#define alloc_thread_stack_node(tsk, node) \
+ ((unsigned long *) ((char *) (tsk) + IA64_TASK_SIZE))
#define task_thread_info(tsk) ((struct thread_info *) ((char *) (tsk) + IA64_TASK_SIZE))
#else
#define current_thread_info() ((struct thread_info *) 0)
-#define alloc_thread_info_node(tsk, node) ((struct thread_info *) 0)
+#define alloc_thread_stack_node(tsk, node) ((unsigned long *) 0)
#define task_thread_info(tsk) ((struct thread_info *) 0)
#endif
-#define free_thread_info(ti) /* nothing */
+#define free_thread_stack(ti) /* nothing */
#define task_stack_page(tsk) ((void *)(tsk))
#define __HAVE_THREAD_FUNCTIONS
diff --git a/arch/ia64/kernel/init_task.c b/arch/ia64/kernel/init_task.c
index f9efe9739d3f..0eaa89f3defd 100644
--- a/arch/ia64/kernel/init_task.c
+++ b/arch/ia64/kernel/init_task.c
@@ -26,6 +26,7 @@ static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
* handled. This is done by having a special ".data..init_task" section...
*/
#define init_thread_info init_task_mem.s.thread_info
+#define init_stack init_task_mem.stack
union {
struct {
diff --git a/arch/mips/include/asm/branch.h b/arch/mips/include/asm/branch.h
index de781cf54bc7..da80878f2c0d 100644
--- a/arch/mips/include/asm/branch.h
+++ b/arch/mips/include/asm/branch.h
@@ -74,10 +74,7 @@ static inline int compute_return_epc(struct pt_regs *regs)
return __microMIPS_compute_return_epc(regs);
if (cpu_has_mips16)
return __MIPS16e_compute_return_epc(regs);
- return regs->cp0_epc;
- }
-
- if (!delay_slot(regs)) {
+ } else if (!delay_slot(regs)) {
regs->cp0_epc += 4;
return 0;
}
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c
index e9fed8ca9b42..71e8f4c0b8da 100644
--- a/arch/mips/kernel/branch.c
+++ b/arch/mips/kernel/branch.c
@@ -399,7 +399,7 @@ int __MIPS16e_compute_return_epc(struct pt_regs *regs)
*
* @regs: Pointer to pt_regs
* @insn: branch instruction to decode
- * @returns: -EFAULT on error and forces SIGBUS, and on success
+ * @returns: -EFAULT on error and forces SIGILL, and on success
* returns 0 or BRANCH_LIKELY_TAKEN as appropriate after
* evaluating the branch.
*
@@ -431,7 +431,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
/* Fall through */
case jr_op:
if (NO_R6EMU && insn.r_format.func == jr_op)
- goto sigill_r6;
+ goto sigill_r2r6;
regs->cp0_epc = regs->regs[insn.r_format.rs];
break;
}
@@ -446,7 +446,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
switch (insn.i_format.rt) {
case bltzl_op:
if (NO_R6EMU)
- goto sigill_r6;
+ goto sigill_r2r6;
case bltz_op:
if ((long)regs->regs[insn.i_format.rs] < 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
@@ -459,7 +459,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
case bgezl_op:
if (NO_R6EMU)
- goto sigill_r6;
+ goto sigill_r2r6;
case bgez_op:
if ((long)regs->regs[insn.i_format.rs] >= 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
@@ -473,10 +473,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
case bltzal_op:
case bltzall_op:
if (NO_R6EMU && (insn.i_format.rs ||
- insn.i_format.rt == bltzall_op)) {
- ret = -SIGILL;
- break;
- }
+ insn.i_format.rt == bltzall_op))
+ goto sigill_r2r6;
regs->regs[31] = epc + 8;
/*
* OK we are here either because we hit a NAL
@@ -507,10 +505,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
case bgezal_op:
case bgezall_op:
if (NO_R6EMU && (insn.i_format.rs ||
- insn.i_format.rt == bgezall_op)) {
- ret = -SIGILL;
- break;
- }
+ insn.i_format.rt == bgezall_op))
+ goto sigill_r2r6;
regs->regs[31] = epc + 8;
/*
* OK we are here either because we hit a BAL
@@ -556,6 +552,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
/*
* These are unconditional and in j_format.
*/
+ case jalx_op:
case jal_op:
regs->regs[31] = regs->cp0_epc + 8;
case j_op:
@@ -573,7 +570,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
*/
case beql_op:
if (NO_R6EMU)
- goto sigill_r6;
+ goto sigill_r2r6;
case beq_op:
if (regs->regs[insn.i_format.rs] ==
regs->regs[insn.i_format.rt]) {
@@ -587,7 +584,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
case bnel_op:
if (NO_R6EMU)
- goto sigill_r6;
+ goto sigill_r2r6;
case bne_op:
if (regs->regs[insn.i_format.rs] !=
regs->regs[insn.i_format.rt]) {
@@ -601,7 +598,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
case blezl_op: /* not really i_format */
if (!insn.i_format.rt && NO_R6EMU)
- goto sigill_r6;
+ goto sigill_r2r6;
case blez_op:
/*
* Compact branches for R6 for the
@@ -636,7 +633,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
case bgtzl_op:
if (!insn.i_format.rt && NO_R6EMU)
- goto sigill_r6;
+ goto sigill_r2r6;
case bgtz_op:
/*
* Compact branches for R6 for the
@@ -843,11 +840,12 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
return ret;
sigill_dsp:
- printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm);
- force_sig(SIGBUS, current);
+ pr_info("%s: DSP branch but not DSP ASE - sending SIGILL.\n",
+ current->comm);
+ force_sig(SIGILL, current);
return -EFAULT;
-sigill_r6:
- pr_info("%s: R2 branch but r2-to-r6 emulator is not preset - sending SIGILL.\n",
+sigill_r2r6:
+ pr_info("%s: R2 branch but r2-to-r6 emulator is not present - sending SIGILL.\n",
current->comm);
force_sig(SIGILL, current);
return -EFAULT;
diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c
index 298b2b773d12..f1fab6ff53e6 100644
--- a/arch/mips/kernel/proc.c
+++ b/arch/mips/kernel/proc.c
@@ -83,7 +83,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
}
seq_printf(m, "isa\t\t\t:");
- if (cpu_has_mips_r1)
+ if (cpu_has_mips_1)
seq_printf(m, " mips1");
if (cpu_has_mips_2)
seq_printf(m, "%s", " mips2");
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index c95bf18260f8..24c115a0721a 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -927,7 +927,7 @@ asmlinkage void syscall_trace_leave(struct pt_regs *regs)
audit_syscall_exit(regs);
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
- trace_sys_exit(regs, regs->regs[2]);
+ trace_sys_exit(regs, regs_return_value(regs));
if (test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall_exit(regs, 0);
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index 2d23c834ba96..29b0c5f978e4 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -372,7 +372,7 @@ EXPORT(sys_call_table)
PTR sys_writev
PTR sys_cacheflush
PTR sys_cachectl
- PTR sys_sysmips
+ PTR __sys_sysmips
PTR sys_ni_syscall /* 4150 */
PTR sys_getsid
PTR sys_fdatasync
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index deac63315d0e..a6323a969919 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -312,7 +312,7 @@ EXPORT(sys_call_table)
PTR sys_sched_getaffinity
PTR sys_cacheflush
PTR sys_cachectl
- PTR sys_sysmips
+ PTR __sys_sysmips
PTR sys_io_setup /* 5200 */
PTR sys_io_destroy
PTR sys_io_getevents
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index ee93d5fe61d7..e0fdca8d3abe 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -298,7 +298,7 @@ EXPORT(sysn32_call_table)
PTR compat_sys_sched_getaffinity
PTR sys_cacheflush
PTR sys_cachectl
- PTR sys_sysmips
+ PTR __sys_sysmips
PTR compat_sys_io_setup /* 6200 */
PTR sys_io_destroy
PTR compat_sys_io_getevents
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index b77052ec6fb2..87c697181d25 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -367,7 +367,7 @@ EXPORT(sys32_call_table)
PTR compat_sys_writev
PTR sys_cacheflush
PTR sys_cachectl
- PTR sys_sysmips
+ PTR __sys_sysmips
PTR sys_ni_syscall /* 4150 */
PTR sys_getsid
PTR sys_fdatasync
diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
index 53a7ef9a8f32..4234b2d726c5 100644
--- a/arch/mips/kernel/syscall.c
+++ b/arch/mips/kernel/syscall.c
@@ -28,6 +28,7 @@
#include <linux/elf.h>
#include <asm/asm.h>
+#include <asm/asm-eva.h>
#include <asm/branch.h>
#include <asm/cachectl.h>
#include <asm/cacheflush.h>
@@ -138,10 +139,12 @@ static inline int mips_atomic_set(unsigned long addr, unsigned long new)
__asm__ __volatile__ (
" .set "MIPS_ISA_ARCH_LEVEL" \n"
" li %[err], 0 \n"
- "1: ll %[old], (%[addr]) \n"
+ "1: \n"
+ user_ll("%[old]", "(%[addr])")
" move %[tmp], %[new] \n"
- "2: sc %[tmp], (%[addr]) \n"
- " bnez %[tmp], 4f \n"
+ "2: \n"
+ user_sc("%[tmp]", "(%[addr])")
+ " beqz %[tmp], 4f \n"
"3: \n"
" .insn \n"
" .subsection 2 \n"
@@ -199,6 +202,12 @@ static inline int mips_atomic_set(unsigned long addr, unsigned long new)
unreachable();
}
+/*
+ * mips_atomic_set() normally returns directly via syscall_exit potentially
+ * clobbering static registers, so be sure to preserve them.
+ */
+save_static_function(sys_sysmips);
+
SYSCALL_DEFINE3(sysmips, long, cmd, long, arg1, long, arg2)
{
switch (cmd) {
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index 734a2c7665ec..6da2e4a6ba39 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -2496,6 +2496,35 @@ dcopuop:
return 0;
}
+/*
+ * Emulate FPU instructions.
+ *
+ * If we use FPU hardware, then we have been typically called to handle
+ * an unimplemented operation, such as where an operand is a NaN or
+ * denormalized. In that case exit the emulation loop after a single
+ * iteration so as to let hardware execute any subsequent instructions.
+ *
+ * If we have no FPU hardware or it has been disabled, then continue
+ * emulating floating-point instructions until one of these conditions
+ * has occurred:
+ *
+ * - a non-FPU instruction has been encountered,
+ *
+ * - an attempt to emulate has ended with a signal,
+ *
+ * - the ISA mode has been switched.
+ *
+ * We need to terminate the emulation loop if we got switched to the
+ * MIPS16 mode, whether supported or not, so that we do not attempt
+ * to emulate a MIPS16 instruction as a regular MIPS FPU instruction.
+ * Similarly if we got switched to the microMIPS mode and only the
+ * regular MIPS mode is supported, so that we do not attempt to emulate
+ * a microMIPS instruction as a regular MIPS FPU instruction. Or if
+ * we got switched to the regular MIPS mode and only the microMIPS mode
+ * is supported, so that we do not attempt to emulate a regular MIPS
+ * instruction that should cause an Address Error exception instead.
+ * For simplicity we always terminate upon an ISA mode switch.
+ */
int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
int has_fpu, void *__user *fault_addr)
{
@@ -2581,6 +2610,15 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
break;
if (sig)
break;
+ /*
+ * We have to check for the ISA bit explicitly here,
+ * because `get_isa16_mode' may return 0 if support
+ * for code compression has been globally disabled,
+ * or otherwise we may produce the wrong signal or
+ * even proceed successfully where we must not.
+ */
+ if ((xcp->cp0_epc ^ prevepc) & 0x1)
+ break;
cond_resched();
} while (xcp->cp0_epc > prevepc);
diff --git a/arch/mn10300/include/asm/thread_info.h b/arch/mn10300/include/asm/thread_info.h
index 4861a78c7160..f5f90bbf019d 100644
--- a/arch/mn10300/include/asm/thread_info.h
+++ b/arch/mn10300/include/asm/thread_info.h
@@ -115,7 +115,7 @@ static inline unsigned long current_stack_pointer(void)
}
#ifndef CONFIG_KGDB
-void arch_release_thread_info(struct thread_info *ti);
+void arch_release_thread_stack(unsigned long *stack);
#endif
#define get_thread_info(ti) get_task_struct((ti)->task)
#define put_thread_info(ti) put_task_struct((ti)->task)
diff --git a/arch/mn10300/kernel/kgdb.c b/arch/mn10300/kernel/kgdb.c
index 99770823451a..2d7986c386fe 100644
--- a/arch/mn10300/kernel/kgdb.c
+++ b/arch/mn10300/kernel/kgdb.c
@@ -397,8 +397,9 @@ static bool kgdb_arch_undo_singlestep(struct pt_regs *regs)
* single-step state is cleared. At this point the breakpoints should have
* been removed by __switch_to().
*/
-void arch_release_thread_info(struct thread_info *ti)
+void arch_release_thread_stack(unsigned long *stack)
{
+ struct thread_info *ti = (void *)stack;
if (kgdb_sstep_thread == ti) {
kgdb_sstep_thread = NULL;
diff --git a/arch/openrisc/kernel/vmlinux.lds.S b/arch/openrisc/kernel/vmlinux.lds.S
index 2d69a853b742..3a08b55609b6 100644
--- a/arch/openrisc/kernel/vmlinux.lds.S
+++ b/arch/openrisc/kernel/vmlinux.lds.S
@@ -38,6 +38,8 @@ SECTIONS
/* Read-only sections, merged into text segment: */
. = LOAD_BASE ;
+ _text = .;
+
/* _s_kernel_ro must be page aligned */
. = ALIGN(PAGE_SIZE);
_s_kernel_ro = .;
diff --git a/arch/parisc/include/asm/dma-mapping.h b/arch/parisc/include/asm/dma-mapping.h
index d8d60a57183f..f53725202955 100644
--- a/arch/parisc/include/asm/dma-mapping.h
+++ b/arch/parisc/include/asm/dma-mapping.h
@@ -39,6 +39,8 @@ struct hppa_dma_ops {
** flush/purge and allocate "regular" cacheable pages for everything.
*/
+#define DMA_ERROR_CODE (~(dma_addr_t)0)
+
#ifdef CONFIG_PA11
extern struct hppa_dma_ops pcxl_dma_ops;
extern struct hppa_dma_ops pcx_dma_ops;
@@ -209,12 +211,13 @@ parisc_walk_tree(struct device *dev)
break;
}
}
- BUG_ON(!dev->platform_data);
return dev->platform_data;
}
-
-#define GET_IOC(dev) (HBA_DATA(parisc_walk_tree(dev))->iommu)
-
+
+#define GET_IOC(dev) ({ \
+ void *__pdata = parisc_walk_tree(dev); \
+ __pdata ? HBA_DATA(__pdata)->iommu : NULL; \
+})
#ifdef CONFIG_IOMMU_CCIO
struct parisc_device;
diff --git a/arch/parisc/include/asm/mmu_context.h b/arch/parisc/include/asm/mmu_context.h
index 59be25764433..a81226257878 100644
--- a/arch/parisc/include/asm/mmu_context.h
+++ b/arch/parisc/include/asm/mmu_context.h
@@ -49,15 +49,26 @@ static inline void load_context(mm_context_t context)
mtctl(__space_to_prot(context), 8);
}
-static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk)
+static inline void switch_mm_irqs_off(struct mm_struct *prev,
+ struct mm_struct *next, struct task_struct *tsk)
{
-
if (prev != next) {
mtctl(__pa(next->pgd), 25);
load_context(next->context);
}
}
+static inline void switch_mm(struct mm_struct *prev,
+ struct mm_struct *next, struct task_struct *tsk)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ switch_mm_irqs_off(prev, next, tsk);
+ local_irq_restore(flags);
+}
+#define switch_mm_irqs_off switch_mm_irqs_off
+
#define deactivate_mm(tsk,mm) do { } while (0)
static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next)
diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S
index d4ffcfbc9885..041e1f9ec129 100644
--- a/arch/parisc/kernel/syscall_table.S
+++ b/arch/parisc/kernel/syscall_table.S
@@ -361,7 +361,7 @@
ENTRY_SAME(ni_syscall) /* 263: reserved for vserver */
ENTRY_SAME(add_key)
ENTRY_SAME(request_key) /* 265 */
- ENTRY_SAME(keyctl)
+ ENTRY_COMP(keyctl)
ENTRY_SAME(ioprio_set)
ENTRY_SAME(ioprio_get)
ENTRY_SAME(inotify_init)
diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c
index 16dbe81c97c9..2f33a67bc531 100644
--- a/arch/parisc/mm/fault.c
+++ b/arch/parisc/mm/fault.c
@@ -298,7 +298,7 @@ bad_area:
case 15: /* Data TLB miss fault/Data page fault */
/* send SIGSEGV when outside of vma */
if (!vma ||
- address < vma->vm_start || address > vma->vm_end) {
+ address < vma->vm_start || address >= vma->vm_end) {
si.si_signo = SIGSEGV;
si.si_code = SEGV_MAPERR;
break;
diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h
index 55f106ed12bf..039c4b910615 100644
--- a/arch/powerpc/include/asm/atomic.h
+++ b/arch/powerpc/include/asm/atomic.h
@@ -460,7 +460,7 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
* Atomically increments @v by 1, so long as @v is non-zero.
* Returns non-zero if @v was non-zero, and zero otherwise.
*/
-static __inline__ long atomic64_inc_not_zero(atomic64_t *v)
+static __inline__ int atomic64_inc_not_zero(atomic64_t *v)
{
long t1, t2;
@@ -479,7 +479,7 @@ static __inline__ long atomic64_inc_not_zero(atomic64_t *v)
: "r" (&v->counter)
: "cc", "xer", "memory");
- return t1;
+ return t1 != 0;
}
#endif /* __powerpc64__ */
diff --git a/arch/powerpc/include/asm/elf.h b/arch/powerpc/include/asm/elf.h
index ee46ffef608e..743ad7a400d6 100644
--- a/arch/powerpc/include/asm/elf.h
+++ b/arch/powerpc/include/asm/elf.h
@@ -23,12 +23,13 @@
#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE PAGE_SIZE
-/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
- use of this is to invoke "./ld.so someprog" to test out a new version of
- the loader. We need to make sure that it is out of the way of the program
- that it will "exec", and that there is sufficient room for the brk. */
-
-#define ELF_ET_DYN_BASE 0x20000000
+/*
+ * This is the base location for PIE (ET_DYN with INTERP) loads. On
+ * 64-bit, this is raised to 4GB to leave the entire 32-bit address
+ * space open for things that want to use the area for 32-bit pointers.
+ */
+#define ELF_ET_DYN_BASE (is_32bit_task() ? 0x000400000UL : \
+ 0x100000000UL)
#define ELF_CORE_EFLAGS (is_elf2_task() ? 2 : 0)
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 627d129d7fcb..ca372bbc0ffe 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -1236,7 +1236,7 @@ static inline unsigned long mfvtb (void)
" .llong 0\n" \
".previous" \
: "=r" (rval) \
- : "i" (CPU_FTR_CELL_TB_BUG), "i" (SPRN_TBRL)); \
+ : "i" (CPU_FTR_CELL_TB_BUG), "i" (SPRN_TBRL) : "cr0"); \
rval;})
#else
#define mftb() ({unsigned long rval; \
diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h
index 329771559cbb..8b3b46b7b0f2 100644
--- a/arch/powerpc/include/asm/topology.h
+++ b/arch/powerpc/include/asm/topology.h
@@ -44,22 +44,8 @@ extern void __init dump_numa_cpu_topology(void);
extern int sysfs_add_device_to_node(struct device *dev, int nid);
extern void sysfs_remove_device_from_node(struct device *dev, int nid);
-static inline int early_cpu_to_node(int cpu)
-{
- int nid;
-
- nid = numa_cpu_lookup_table[cpu];
-
- /*
- * Fall back to node 0 if nid is unset (it should be, except bugs).
- * This allows callers to safely do NODE_DATA(early_cpu_to_node(cpu)).
- */
- return (nid < 0) ? 0 : nid;
-}
#else
-static inline int early_cpu_to_node(int cpu) { return 0; }
-
static inline void dump_numa_cpu_topology(void) {}
static inline int sysfs_add_device_to_node(struct device *dev, int nid)
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index fe6e800c1357..a20823210ac0 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -751,7 +751,7 @@ void __init setup_arch(char **cmdline_p)
static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size, size_t align)
{
- return __alloc_bootmem_node(NODE_DATA(early_cpu_to_node(cpu)), size, align,
+ return __alloc_bootmem_node(NODE_DATA(cpu_to_node(cpu)), size, align,
__pa(MAX_DMA_ADDRESS));
}
@@ -762,7 +762,7 @@ static void __init pcpu_fc_free(void *ptr, size_t size)
static int pcpu_cpu_distance(unsigned int from, unsigned int to)
{
- if (early_cpu_to_node(from) == early_cpu_to_node(to))
+ if (cpu_to_node(from) == cpu_to_node(to))
return LOCAL_DISTANCE;
else
return REMOTE_DISTANCE;
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 396dc44e783b..428563b195c3 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -2687,6 +2687,10 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
{
int r;
int srcu_idx;
+ unsigned long ebb_regs[3] = {}; /* shut up GCC */
+ unsigned long user_tar = 0;
+ unsigned long proc_fscr = 0;
+ unsigned int user_vrsave;
if (!vcpu->arch.sane) {
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
@@ -2707,10 +2711,11 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
run->fail_entry.hardware_entry_failure_reason = 0;
return -EINVAL;
}
+ /* Enable TM so we can read the TM SPRs */
+ mtmsr(mfmsr() | MSR_TM);
current->thread.tm_tfhar = mfspr(SPRN_TFHAR);
current->thread.tm_tfiar = mfspr(SPRN_TFIAR);
current->thread.tm_texasr = mfspr(SPRN_TEXASR);
- current->thread.regs->msr &= ~MSR_TM;
}
#endif
@@ -2736,6 +2741,17 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
flush_fp_to_thread(current);
flush_altivec_to_thread(current);
flush_vsx_to_thread(current);
+
+ /* Save userspace EBB and other register values */
+ if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
+ ebb_regs[0] = mfspr(SPRN_EBBHR);
+ ebb_regs[1] = mfspr(SPRN_EBBRR);
+ ebb_regs[2] = mfspr(SPRN_BESCR);
+ user_tar = mfspr(SPRN_TAR);
+ proc_fscr = mfspr(SPRN_FSCR);
+ }
+ user_vrsave = mfspr(SPRN_VRSAVE);
+
vcpu->arch.wqp = &vcpu->arch.vcore->wq;
vcpu->arch.pgdir = current->mm->pgd;
vcpu->arch.state = KVMPPC_VCPU_BUSY_IN_HOST;
@@ -2757,6 +2773,29 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
}
} while (is_kvmppc_resume_guest(r));
+ /* Restore userspace EBB and other register values */
+ if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
+ mtspr(SPRN_EBBHR, ebb_regs[0]);
+ mtspr(SPRN_EBBRR, ebb_regs[1]);
+ mtspr(SPRN_BESCR, ebb_regs[2]);
+ mtspr(SPRN_TAR, user_tar);
+ mtspr(SPRN_FSCR, proc_fscr);
+ }
+ mtspr(SPRN_VRSAVE, user_vrsave);
+
+ /*
+ * Since we don't do lazy TM reload, we need to reload
+ * the TM registers here.
+ */
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ if (cpu_has_feature(CPU_FTR_TM) && current->thread.regs &&
+ (current->thread.regs->msr & MSR_TM)) {
+ mtspr(SPRN_TFHAR, current->thread.tm_tfhar);
+ mtspr(SPRN_TFIAR, current->thread.tm_tfiar);
+ mtspr(SPRN_TEXASR, current->thread.tm_texasr);
+ }
+#endif
+
out:
vcpu->arch.state = KVMPPC_VCPU_NOTREADY;
atomic_dec(&vcpu->kvm->arch.vcpus_running);
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index 1a743f87b37d..ffab9269bfe4 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -36,6 +36,13 @@
#define NAPPING_CEDE 1
#define NAPPING_NOVCPU 2
+/* Stack frame offsets for kvmppc_hv_entry */
+#define SFS 112
+#define STACK_SLOT_TRAP (SFS-4)
+#define STACK_SLOT_CIABR (SFS-16)
+#define STACK_SLOT_DAWR (SFS-24)
+#define STACK_SLOT_DAWRX (SFS-32)
+
/*
* Call kvmppc_hv_entry in real mode.
* Must be called with interrupts hard-disabled.
@@ -274,10 +281,10 @@ kvm_novcpu_exit:
bl kvmhv_accumulate_time
#endif
13: mr r3, r12
- stw r12, 112-4(r1)
+ stw r12, STACK_SLOT_TRAP(r1)
bl kvmhv_commence_exit
nop
- lwz r12, 112-4(r1)
+ lwz r12, STACK_SLOT_TRAP(r1)
b kvmhv_switch_to_host
/*
@@ -489,7 +496,7 @@ kvmppc_hv_entry:
*/
mflr r0
std r0, PPC_LR_STKOFF(r1)
- stdu r1, -112(r1)
+ stdu r1, -SFS(r1)
/* Save R1 in the PACA */
std r1, HSTATE_HOST_R1(r13)
@@ -643,6 +650,16 @@ kvmppc_got_guest:
mtspr SPRN_PURR,r7
mtspr SPRN_SPURR,r8
+ /* Save host values of some registers */
+BEGIN_FTR_SECTION
+ mfspr r5, SPRN_CIABR
+ mfspr r6, SPRN_DAWR
+ mfspr r7, SPRN_DAWRX
+ std r5, STACK_SLOT_CIABR(r1)
+ std r6, STACK_SLOT_DAWR(r1)
+ std r7, STACK_SLOT_DAWRX(r1)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
+
BEGIN_FTR_SECTION
/* Set partition DABR */
/* Do this before re-enabling PMU to avoid P7 DABR corruption bug */
@@ -1266,8 +1283,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
*/
li r0, 0
mtspr SPRN_IAMR, r0
- mtspr SPRN_CIABR, r0
- mtspr SPRN_DAWRX, r0
+ mtspr SPRN_PSPB, r0
mtspr SPRN_TCSCR, r0
mtspr SPRN_WORT, r0
/* Set MMCRS to 1<<31 to freeze and disable the SPMC counters */
@@ -1283,6 +1299,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
std r6,VCPU_UAMOR(r9)
li r6,0
mtspr SPRN_AMR,r6
+ mtspr SPRN_UAMOR, r6
/* Switch DSCR back to host value */
mfspr r8, SPRN_DSCR
@@ -1424,6 +1441,16 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
slbia
ptesync
+ /* Restore host values of some registers */
+BEGIN_FTR_SECTION
+ ld r5, STACK_SLOT_CIABR(r1)
+ ld r6, STACK_SLOT_DAWR(r1)
+ ld r7, STACK_SLOT_DAWRX(r1)
+ mtspr SPRN_CIABR, r5
+ mtspr SPRN_DAWR, r6
+ mtspr SPRN_DAWRX, r7
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
+
/*
* POWER7/POWER8 guest -> host partition switch code.
* We don't have to lock against tlbies but we do
@@ -1533,8 +1560,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
li r0, KVM_GUEST_MODE_NONE
stb r0, HSTATE_IN_GUEST(r13)
- ld r0, 112+PPC_LR_STKOFF(r1)
- addi r1, r1, 112
+ ld r0, SFS+PPC_LR_STKOFF(r1)
+ addi r1, r1, SFS
mtlr r0
blr
diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
index 4014881e9843..e37162d356d8 100644
--- a/arch/powerpc/lib/sstep.c
+++ b/arch/powerpc/lib/sstep.c
@@ -687,8 +687,10 @@ int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs,
case 19:
switch ((instr >> 1) & 0x3ff) {
case 0: /* mcrf */
- rd = (instr >> 21) & 0x1c;
- ra = (instr >> 16) & 0x1c;
+ rd = 7 - ((instr >> 23) & 0x7);
+ ra = 7 - ((instr >> 18) & 0x7);
+ rd *= 4;
+ ra *= 4;
val = (regs->ccr >> ra) & 0xf;
regs->ccr = (regs->ccr & ~(0xfUL << rd)) | (val << rd);
goto instr_done;
@@ -967,6 +969,19 @@ int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs,
#endif
case 19: /* mfcr */
+ if ((instr >> 20) & 1) {
+ imm = 0xf0000000UL;
+ for (sh = 0; sh < 8; ++sh) {
+ if (instr & (0x80000 >> sh)) {
+ regs->gpr[rd] = regs->ccr & imm;
+ break;
+ }
+ imm >>= 4;
+ }
+
+ goto instr_done;
+ }
+
regs->gpr[rd] = regs->ccr;
regs->gpr[rd] &= 0xffffffffUL;
goto instr_done;
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
index 7c7fcc042549..fb695f142563 100644
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -82,7 +82,6 @@ static int pSeries_reconfig_remove_node(struct device_node *np)
of_detach_node(np);
of_node_put(parent);
- of_node_put(np); /* Must decrement the refcount */
return 0;
}
diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h
index bab6739a1154..b9eb7b1a49d2 100644
--- a/arch/s390/include/asm/elf.h
+++ b/arch/s390/include/asm/elf.h
@@ -154,14 +154,13 @@ extern unsigned int vdso_enabled;
#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE 4096
-/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
- use of this is to invoke "./ld.so someprog" to test out a new version of
- the loader. We need to make sure that it is out of the way of the program
- that it will "exec", and that there is sufficient room for the brk. 64-bit
- tasks are aligned to 4GB. */
-#define ELF_ET_DYN_BASE (is_32bit_task() ? \
- (STACK_TOP / 3 * 2) : \
- (STACK_TOP / 3 * 2) & ~((1UL << 32) - 1))
+/*
+ * This is the base location for PIE (ET_DYN with INTERP) loads. On
+ * 64-bit, this is raised to 4GB to leave the entire 32-bit address
+ * space open for things that want to use the area for 32-bit pointers.
+ */
+#define ELF_ET_DYN_BASE (is_compat_task() ? 0x000400000UL : \
+ 0x100000000UL)
/* This yields a mask that user programs can use to figure out what
instruction set this CPU supports. */
diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
index 6ba0bf928909..6bc941be6921 100644
--- a/arch/s390/include/asm/syscall.h
+++ b/arch/s390/include/asm/syscall.h
@@ -64,6 +64,12 @@ static inline void syscall_get_arguments(struct task_struct *task,
{
unsigned long mask = -1UL;
+ /*
+ * No arguments for this syscall, there's nothing to do.
+ */
+ if (!n)
+ return;
+
BUG_ON(i + n > 6);
#ifdef CONFIG_COMPAT
if (test_tsk_thread_flag(task, TIF_31BIT))
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index 0e2919dd8df3..1395eeb6005f 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -1250,7 +1250,8 @@ static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp)
insn_count = bpf_jit_insn(jit, fp, i);
if (insn_count < 0)
return -1;
- jit->addrs[i + 1] = jit->prg; /* Next instruction address */
+ /* Next instruction address */
+ jit->addrs[i + insn_count] = jit->prg;
}
bpf_jit_epilogue(jit);
diff --git a/arch/sparc/include/asm/mmu_context_64.h b/arch/sparc/include/asm/mmu_context_64.h
index 349dd23e2876..0cdeb2b483a0 100644
--- a/arch/sparc/include/asm/mmu_context_64.h
+++ b/arch/sparc/include/asm/mmu_context_64.h
@@ -25,9 +25,11 @@ void destroy_context(struct mm_struct *mm);
void __tsb_context_switch(unsigned long pgd_pa,
struct tsb_config *tsb_base,
struct tsb_config *tsb_huge,
- unsigned long tsb_descr_pa);
+ unsigned long tsb_descr_pa,
+ unsigned long secondary_ctx);
-static inline void tsb_context_switch(struct mm_struct *mm)
+static inline void tsb_context_switch_ctx(struct mm_struct *mm,
+ unsigned long ctx)
{
__tsb_context_switch(__pa(mm->pgd),
&mm->context.tsb_block[0],
@@ -38,9 +40,12 @@ static inline void tsb_context_switch(struct mm_struct *mm)
#else
NULL
#endif
- , __pa(&mm->context.tsb_descr[0]));
+ , __pa(&mm->context.tsb_descr[0]),
+ ctx);
}
+#define tsb_context_switch(X) tsb_context_switch_ctx(X, 0)
+
void tsb_grow(struct mm_struct *mm,
unsigned long tsb_index,
unsigned long mm_rss);
@@ -110,8 +115,7 @@ static inline void switch_mm(struct mm_struct *old_mm, struct mm_struct *mm, str
* cpu0 to update it's TSB because at that point the cpu_vm_mask
* only had cpu1 set in it.
*/
- load_secondary_context(mm);
- tsb_context_switch(mm);
+ tsb_context_switch_ctx(mm, CTX_HWBITS(mm->context));
/* Any time a processor runs a context on an address space
* for the first time, we must flush that context out of the
diff --git a/arch/sparc/include/asm/trap_block.h b/arch/sparc/include/asm/trap_block.h
index ec9c04de3664..ff05992dae7a 100644
--- a/arch/sparc/include/asm/trap_block.h
+++ b/arch/sparc/include/asm/trap_block.h
@@ -54,6 +54,7 @@ extern struct trap_per_cpu trap_block[NR_CPUS];
void init_cur_cpu_trap(struct thread_info *);
void setup_tba(void);
extern int ncpus_probed;
+extern u64 cpu_mondo_counter[NR_CPUS];
unsigned long real_hard_smp_processor_id(void);
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index 95a9fa0d2195..4511caa3b7e9 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -617,22 +617,48 @@ retry:
}
}
-/* Multi-cpu list version. */
+#define CPU_MONDO_COUNTER(cpuid) (cpu_mondo_counter[cpuid])
+#define MONDO_USEC_WAIT_MIN 2
+#define MONDO_USEC_WAIT_MAX 100
+#define MONDO_RETRY_LIMIT 500000
+
+/* Multi-cpu list version.
+ *
+ * Deliver xcalls to 'cnt' number of cpus in 'cpu_list'.
+ * Sometimes not all cpus receive the mondo, requiring us to re-send
+ * the mondo until all cpus have received, or cpus are truly stuck
+ * unable to receive mondo, and we timeout.
+ * Occasionally a target cpu strand is borrowed briefly by hypervisor to
+ * perform guest service, such as PCIe error handling. Consider the
+ * service time, 1 second overall wait is reasonable for 1 cpu.
+ * Here two in-between mondo check wait time are defined: 2 usec for
+ * single cpu quick turn around and up to 100usec for large cpu count.
+ * Deliver mondo to large number of cpus could take longer, we adjusts
+ * the retry count as long as target cpus are making forward progress.
+ */
static void hypervisor_xcall_deliver(struct trap_per_cpu *tb, int cnt)
{
- int retries, this_cpu, prev_sent, i, saw_cpu_error;
+ int this_cpu, tot_cpus, prev_sent, i, rem;
+ int usec_wait, retries, tot_retries;
+ u16 first_cpu = 0xffff;
+ unsigned long xc_rcvd = 0;
unsigned long status;
+ int ecpuerror_id = 0;
+ int enocpu_id = 0;
u16 *cpu_list;
+ u16 cpu;
this_cpu = smp_processor_id();
-
cpu_list = __va(tb->cpu_list_pa);
-
- saw_cpu_error = 0;
- retries = 0;
+ usec_wait = cnt * MONDO_USEC_WAIT_MIN;
+ if (usec_wait > MONDO_USEC_WAIT_MAX)
+ usec_wait = MONDO_USEC_WAIT_MAX;
+ retries = tot_retries = 0;
+ tot_cpus = cnt;
prev_sent = 0;
+
do {
- int forward_progress, n_sent;
+ int n_sent, mondo_delivered, target_cpu_busy;
status = sun4v_cpu_mondo_send(cnt,
tb->cpu_list_pa,
@@ -640,94 +666,113 @@ static void hypervisor_xcall_deliver(struct trap_per_cpu *tb, int cnt)
/* HV_EOK means all cpus received the xcall, we're done. */
if (likely(status == HV_EOK))
- break;
+ goto xcall_done;
+
+ /* If not these non-fatal errors, panic */
+ if (unlikely((status != HV_EWOULDBLOCK) &&
+ (status != HV_ECPUERROR) &&
+ (status != HV_ENOCPU)))
+ goto fatal_errors;
/* First, see if we made any forward progress.
*
+ * Go through the cpu_list, count the target cpus that have
+ * received our mondo (n_sent), and those that did not (rem).
+ * Re-pack cpu_list with the cpus remain to be retried in the
+ * front - this simplifies tracking the truly stalled cpus.
+ *
* The hypervisor indicates successful sends by setting
* cpu list entries to the value 0xffff.
+ *
+ * EWOULDBLOCK means some target cpus did not receive the
+ * mondo and retry usually helps.
+ *
+ * ECPUERROR means at least one target cpu is in error state,
+ * it's usually safe to skip the faulty cpu and retry.
+ *
+ * ENOCPU means one of the target cpu doesn't belong to the
+ * domain, perhaps offlined which is unexpected, but not
+ * fatal and it's okay to skip the offlined cpu.
*/
+ rem = 0;
n_sent = 0;
for (i = 0; i < cnt; i++) {
- if (likely(cpu_list[i] == 0xffff))
+ cpu = cpu_list[i];
+ if (likely(cpu == 0xffff)) {
n_sent++;
+ } else if ((status == HV_ECPUERROR) &&
+ (sun4v_cpu_state(cpu) == HV_CPU_STATE_ERROR)) {
+ ecpuerror_id = cpu + 1;
+ } else if (status == HV_ENOCPU && !cpu_online(cpu)) {
+ enocpu_id = cpu + 1;
+ } else {
+ cpu_list[rem++] = cpu;
+ }
}
- forward_progress = 0;
- if (n_sent > prev_sent)
- forward_progress = 1;
+ /* No cpu remained, we're done. */
+ if (rem == 0)
+ break;
- prev_sent = n_sent;
+ /* Otherwise, update the cpu count for retry. */
+ cnt = rem;
- /* If we get a HV_ECPUERROR, then one or more of the cpus
- * in the list are in error state. Use the cpu_state()
- * hypervisor call to find out which cpus are in error state.
+ /* Record the overall number of mondos received by the
+ * first of the remaining cpus.
*/
- if (unlikely(status == HV_ECPUERROR)) {
- for (i = 0; i < cnt; i++) {
- long err;
- u16 cpu;
+ if (first_cpu != cpu_list[0]) {
+ first_cpu = cpu_list[0];
+ xc_rcvd = CPU_MONDO_COUNTER(first_cpu);
+ }
- cpu = cpu_list[i];
- if (cpu == 0xffff)
- continue;
+ /* Was any mondo delivered successfully? */
+ mondo_delivered = (n_sent > prev_sent);
+ prev_sent = n_sent;
- err = sun4v_cpu_state(cpu);
- if (err == HV_CPU_STATE_ERROR) {
- saw_cpu_error = (cpu + 1);
- cpu_list[i] = 0xffff;
- }
- }
- } else if (unlikely(status != HV_EWOULDBLOCK))
- goto fatal_mondo_error;
+ /* or, was any target cpu busy processing other mondos? */
+ target_cpu_busy = (xc_rcvd < CPU_MONDO_COUNTER(first_cpu));
+ xc_rcvd = CPU_MONDO_COUNTER(first_cpu);
- /* Don't bother rewriting the CPU list, just leave the
- * 0xffff and non-0xffff entries in there and the
- * hypervisor will do the right thing.
- *
- * Only advance timeout state if we didn't make any
- * forward progress.
+ /* Retry count is for no progress. If we're making progress,
+ * reset the retry count.
*/
- if (unlikely(!forward_progress)) {
- if (unlikely(++retries > 10000))
- goto fatal_mondo_timeout;
-
- /* Delay a little bit to let other cpus catch up
- * on their cpu mondo queue work.
- */
- udelay(2 * cnt);
+ if (likely(mondo_delivered || target_cpu_busy)) {
+ tot_retries += retries;
+ retries = 0;
+ } else if (unlikely(retries > MONDO_RETRY_LIMIT)) {
+ goto fatal_mondo_timeout;
}
- } while (1);
- if (unlikely(saw_cpu_error))
- goto fatal_mondo_cpu_error;
+ /* Delay a little bit to let other cpus catch up on
+ * their cpu mondo queue work.
+ */
+ if (!mondo_delivered)
+ udelay(usec_wait);
- return;
+ retries++;
+ } while (1);
-fatal_mondo_cpu_error:
- printk(KERN_CRIT "CPU[%d]: SUN4V mondo cpu error, some target cpus "
- "(including %d) were in error state\n",
- this_cpu, saw_cpu_error - 1);
+xcall_done:
+ if (unlikely(ecpuerror_id > 0)) {
+ pr_crit("CPU[%d]: SUN4V mondo cpu error, target cpu(%d) was in error state\n",
+ this_cpu, ecpuerror_id - 1);
+ } else if (unlikely(enocpu_id > 0)) {
+ pr_crit("CPU[%d]: SUN4V mondo cpu error, target cpu(%d) does not belong to the domain\n",
+ this_cpu, enocpu_id - 1);
+ }
return;
+fatal_errors:
+ /* fatal errors include bad alignment, etc */
+ pr_crit("CPU[%d]: Args were cnt(%d) cpulist_pa(%lx) mondo_block_pa(%lx)\n",
+ this_cpu, tot_cpus, tb->cpu_list_pa, tb->cpu_mondo_block_pa);
+ panic("Unexpected SUN4V mondo error %lu\n", status);
+
fatal_mondo_timeout:
- printk(KERN_CRIT "CPU[%d]: SUN4V mondo timeout, no forward "
- " progress after %d retries.\n",
- this_cpu, retries);
- goto dump_cpu_list_and_out;
-
-fatal_mondo_error:
- printk(KERN_CRIT "CPU[%d]: Unexpected SUN4V mondo error %lu\n",
- this_cpu, status);
- printk(KERN_CRIT "CPU[%d]: Args were cnt(%d) cpulist_pa(%lx) "
- "mondo_block_pa(%lx)\n",
- this_cpu, cnt, tb->cpu_list_pa, tb->cpu_mondo_block_pa);
-
-dump_cpu_list_and_out:
- printk(KERN_CRIT "CPU[%d]: CPU list [ ", this_cpu);
- for (i = 0; i < cnt; i++)
- printk("%u ", cpu_list[i]);
- printk("]\n");
+ /* some cpus being non-responsive to the cpu mondo */
+ pr_crit("CPU[%d]: SUN4V mondo timeout, cpu(%d) made no forward progress after %d retries. Total target cpus(%d).\n",
+ this_cpu, first_cpu, (tot_retries + retries), tot_cpus);
+ panic("SUN4V mondo timeout panic\n");
}
static void (*xcall_deliver_impl)(struct trap_per_cpu *, int);
diff --git a/arch/sparc/kernel/sun4v_ivec.S b/arch/sparc/kernel/sun4v_ivec.S
index 559bc5e9c199..34631995859a 100644
--- a/arch/sparc/kernel/sun4v_ivec.S
+++ b/arch/sparc/kernel/sun4v_ivec.S
@@ -26,6 +26,21 @@ sun4v_cpu_mondo:
ldxa [%g0] ASI_SCRATCHPAD, %g4
sub %g4, TRAP_PER_CPU_FAULT_INFO, %g4
+ /* Get smp_processor_id() into %g3 */
+ sethi %hi(trap_block), %g5
+ or %g5, %lo(trap_block), %g5
+ sub %g4, %g5, %g3
+ srlx %g3, TRAP_BLOCK_SZ_SHIFT, %g3
+
+ /* Increment cpu_mondo_counter[smp_processor_id()] */
+ sethi %hi(cpu_mondo_counter), %g5
+ or %g5, %lo(cpu_mondo_counter), %g5
+ sllx %g3, 3, %g3
+ add %g5, %g3, %g5
+ ldx [%g5], %g3
+ add %g3, 1, %g3
+ stx %g3, [%g5]
+
/* Get CPU mondo queue base phys address into %g7. */
ldx [%g4 + TRAP_PER_CPU_CPU_MONDO_PA], %g7
diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c
index cc97a43268ee..d883c5951e8b 100644
--- a/arch/sparc/kernel/traps_64.c
+++ b/arch/sparc/kernel/traps_64.c
@@ -2659,6 +2659,7 @@ void do_getpsr(struct pt_regs *regs)
}
}
+u64 cpu_mondo_counter[NR_CPUS] = {0};
struct trap_per_cpu trap_block[NR_CPUS];
EXPORT_SYMBOL(trap_block);
diff --git a/arch/sparc/kernel/tsb.S b/arch/sparc/kernel/tsb.S
index 395ec1800530..7d961f6e3907 100644
--- a/arch/sparc/kernel/tsb.S
+++ b/arch/sparc/kernel/tsb.S
@@ -375,6 +375,7 @@ tsb_flush:
* %o1: TSB base config pointer
* %o2: TSB huge config pointer, or NULL if none
* %o3: Hypervisor TSB descriptor physical address
+ * %o4: Secondary context to load, if non-zero
*
* We have to run this whole thing with interrupts
* disabled so that the current cpu doesn't change
@@ -387,6 +388,17 @@ __tsb_context_switch:
rdpr %pstate, %g1
wrpr %g1, PSTATE_IE, %pstate
+ brz,pn %o4, 1f
+ mov SECONDARY_CONTEXT, %o5
+
+661: stxa %o4, [%o5] ASI_DMMU
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ stxa %o4, [%o5] ASI_MMU
+ .previous
+ flush %g6
+
+1:
TRAP_LOAD_TRAP_BLOCK(%g2, %g3)
stx %o0, [%g2 + TRAP_PER_CPU_PGD_PADDR]
diff --git a/arch/sparc/power/hibernate.c b/arch/sparc/power/hibernate.c
index 17bd2e167e07..df707a8ad311 100644
--- a/arch/sparc/power/hibernate.c
+++ b/arch/sparc/power/hibernate.c
@@ -35,6 +35,5 @@ void restore_processor_state(void)
{
struct mm_struct *mm = current->active_mm;
- load_secondary_context(mm);
- tsb_context_switch(mm);
+ tsb_context_switch_ctx(mm, CTX_HWBITS(mm->context));
}
diff --git a/arch/tile/include/asm/thread_info.h b/arch/tile/include/asm/thread_info.h
index dc1fb28d9636..489b15016303 100644
--- a/arch/tile/include/asm/thread_info.h
+++ b/arch/tile/include/asm/thread_info.h
@@ -78,7 +78,7 @@ struct thread_info {
#ifndef __ASSEMBLY__
-void arch_release_thread_info(struct thread_info *info);
+void arch_release_thread_stack(unsigned long *stack);
/* How to get the thread information struct from C. */
register unsigned long stack_pointer __asm__("sp");
diff --git a/arch/tile/kernel/process.c b/arch/tile/kernel/process.c
index 7d5769310bef..a97ab1a69a90 100644
--- a/arch/tile/kernel/process.c
+++ b/arch/tile/kernel/process.c
@@ -73,8 +73,9 @@ void arch_cpu_idle(void)
/*
* Release a thread_info structure
*/
-void arch_release_thread_info(struct thread_info *info)
+void arch_release_thread_stack(unsigned long *stack)
{
+ struct thread_info *info = (void *)stack;
struct single_step_state *step_state = info->step_state;
if (step_state) {
diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
index 318b8465d302..06ceddb3a22e 100644
--- a/arch/x86/boot/string.c
+++ b/arch/x86/boot/string.c
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include "ctype.h"
+#include "string.h"
int memcmp(const void *s1, const void *s2, size_t len)
{
diff --git a/arch/x86/boot/string.h b/arch/x86/boot/string.h
index 725e820602b1..113588ddb43f 100644
--- a/arch/x86/boot/string.h
+++ b/arch/x86/boot/string.h
@@ -18,4 +18,13 @@ int memcmp(const void *s1, const void *s2, size_t len);
#define memset(d,c,l) __builtin_memset(d,c,l)
#define memcmp __builtin_memcmp
+extern int strcmp(const char *str1, const char *str2);
+extern int strncmp(const char *cs, const char *ct, size_t count);
+extern size_t strlen(const char *s);
+extern char *strstr(const char *s1, const char *s2);
+extern size_t strnlen(const char *s, size_t maxlen);
+extern unsigned int atou(const char *s);
+extern unsigned long long simple_strtoull(const char *cp, char **endp,
+ unsigned int base);
+
#endif /* BOOT_STRING_H */
diff --git a/arch/x86/crypto/sha1_avx2_x86_64_asm.S b/arch/x86/crypto/sha1_avx2_x86_64_asm.S
index 1cd792db15ef..1eab79c9ac48 100644
--- a/arch/x86/crypto/sha1_avx2_x86_64_asm.S
+++ b/arch/x86/crypto/sha1_avx2_x86_64_asm.S
@@ -117,11 +117,10 @@
.set T1, REG_T1
.endm
-#define K_BASE %r8
#define HASH_PTR %r9
+#define BLOCKS_CTR %r8
#define BUFFER_PTR %r10
#define BUFFER_PTR2 %r13
-#define BUFFER_END %r11
#define PRECALC_BUF %r14
#define WK_BUF %r15
@@ -205,14 +204,14 @@
* blended AVX2 and ALU instruction scheduling
* 1 vector iteration per 8 rounds
*/
- vmovdqu ((i * 2) + PRECALC_OFFSET)(BUFFER_PTR), W_TMP
+ vmovdqu (i * 2)(BUFFER_PTR), W_TMP
.elseif ((i & 7) == 1)
- vinsertf128 $1, (((i-1) * 2)+PRECALC_OFFSET)(BUFFER_PTR2),\
+ vinsertf128 $1, ((i-1) * 2)(BUFFER_PTR2),\
WY_TMP, WY_TMP
.elseif ((i & 7) == 2)
vpshufb YMM_SHUFB_BSWAP, WY_TMP, WY
.elseif ((i & 7) == 4)
- vpaddd K_XMM(K_BASE), WY, WY_TMP
+ vpaddd K_XMM + K_XMM_AR(%rip), WY, WY_TMP
.elseif ((i & 7) == 7)
vmovdqu WY_TMP, PRECALC_WK(i&~7)
@@ -255,7 +254,7 @@
vpxor WY, WY_TMP, WY_TMP
.elseif ((i & 7) == 7)
vpxor WY_TMP2, WY_TMP, WY
- vpaddd K_XMM(K_BASE), WY, WY_TMP
+ vpaddd K_XMM + K_XMM_AR(%rip), WY, WY_TMP
vmovdqu WY_TMP, PRECALC_WK(i&~7)
PRECALC_ROTATE_WY
@@ -291,7 +290,7 @@
vpsrld $30, WY, WY
vpor WY, WY_TMP, WY
.elseif ((i & 7) == 7)
- vpaddd K_XMM(K_BASE), WY, WY_TMP
+ vpaddd K_XMM + K_XMM_AR(%rip), WY, WY_TMP
vmovdqu WY_TMP, PRECALC_WK(i&~7)
PRECALC_ROTATE_WY
@@ -446,6 +445,16 @@
.endm
+/* Add constant only if (%2 > %3) condition met (uses RTA as temp)
+ * %1 + %2 >= %3 ? %4 : 0
+ */
+.macro ADD_IF_GE a, b, c, d
+ mov \a, RTA
+ add $\d, RTA
+ cmp $\c, \b
+ cmovge RTA, \a
+.endm
+
/*
* macro implements 80 rounds of SHA-1, for multiple blocks with s/w pipelining
*/
@@ -463,13 +472,16 @@
lea (2*4*80+32)(%rsp), WK_BUF
# Precalc WK for first 2 blocks
- PRECALC_OFFSET = 0
+ ADD_IF_GE BUFFER_PTR2, BLOCKS_CTR, 2, 64
.set i, 0
.rept 160
PRECALC i
.set i, i + 1
.endr
- PRECALC_OFFSET = 128
+
+ /* Go to next block if needed */
+ ADD_IF_GE BUFFER_PTR, BLOCKS_CTR, 3, 128
+ ADD_IF_GE BUFFER_PTR2, BLOCKS_CTR, 4, 128
xchg WK_BUF, PRECALC_BUF
.align 32
@@ -479,8 +491,8 @@ _loop:
* we use K_BASE value as a signal of a last block,
* it is set below by: cmovae BUFFER_PTR, K_BASE
*/
- cmp K_BASE, BUFFER_PTR
- jne _begin
+ test BLOCKS_CTR, BLOCKS_CTR
+ jnz _begin
.align 32
jmp _end
.align 32
@@ -512,10 +524,10 @@ _loop0:
.set j, j+2
.endr
- add $(2*64), BUFFER_PTR /* move to next odd-64-byte block */
- cmp BUFFER_END, BUFFER_PTR /* is current block the last one? */
- cmovae K_BASE, BUFFER_PTR /* signal the last iteration smartly */
-
+ /* Update Counter */
+ sub $1, BLOCKS_CTR
+ /* Move to the next block only if needed*/
+ ADD_IF_GE BUFFER_PTR, BLOCKS_CTR, 4, 128
/*
* rounds
* 60,62,64,66,68
@@ -532,8 +544,8 @@ _loop0:
UPDATE_HASH 12(HASH_PTR), D
UPDATE_HASH 16(HASH_PTR), E
- cmp K_BASE, BUFFER_PTR /* is current block the last one? */
- je _loop
+ test BLOCKS_CTR, BLOCKS_CTR
+ jz _loop
mov TB, B
@@ -575,10 +587,10 @@ _loop2:
.set j, j+2
.endr
- add $(2*64), BUFFER_PTR2 /* move to next even-64-byte block */
-
- cmp BUFFER_END, BUFFER_PTR2 /* is current block the last one */
- cmovae K_BASE, BUFFER_PTR /* signal the last iteration smartly */
+ /* update counter */
+ sub $1, BLOCKS_CTR
+ /* Move to the next block only if needed*/
+ ADD_IF_GE BUFFER_PTR2, BLOCKS_CTR, 4, 128
jmp _loop3
_loop3:
@@ -641,19 +653,12 @@ _loop3:
avx2_zeroupper
- lea K_XMM_AR(%rip), K_BASE
-
+ /* Setup initial values */
mov CTX, HASH_PTR
mov BUF, BUFFER_PTR
- lea 64(BUF), BUFFER_PTR2
-
- shl $6, CNT /* mul by 64 */
- add BUF, CNT
- add $64, CNT
- mov CNT, BUFFER_END
- cmp BUFFER_END, BUFFER_PTR2
- cmovae K_BASE, BUFFER_PTR2
+ mov BUF, BUFFER_PTR2
+ mov CNT, BLOCKS_CTR
xmm_mov BSWAP_SHUFB_CTL(%rip), YMM_SHUFB_BSWAP
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index a55697d19824..cc0f2f5da19b 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -1190,6 +1190,8 @@ ENTRY(nmi)
* other IST entries.
*/
+ ASM_CLAC
+
/* Use %rdx as our temp variable throughout */
pushq %rdx
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index d262f985bbc8..bcd3d6199464 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -245,12 +245,13 @@ extern int force_personality32;
#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE 4096
-/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
- use of this is to invoke "./ld.so someprog" to test out a new version of
- the loader. We need to make sure that it is out of the way of the program
- that it will "exec", and that there is sufficient room for the brk. */
-
-#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2)
+/*
+ * This is the base location for PIE (ET_DYN with INTERP) loads. On
+ * 64-bit, this is above 4GB to leave the entire 32-bit address
+ * space open for things that want to use the area for 32-bit pointers.
+ */
+#define ELF_ET_DYN_BASE (mmap_is_ia32() ? 0x000400000UL : \
+ (TASK_SIZE / 3 * 2))
/* This yields a mask that user programs can use to figure out what
instruction set this CPU supports. This could be done in user space,
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 690b4027e17c..37db36fddc88 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -405,6 +405,8 @@
#define MSR_IA32_TSC_ADJUST 0x0000003b
#define MSR_IA32_BNDCFGS 0x00000d90
+#define MSR_IA32_BNDCFGS_RSVD 0x00000ffc
+
#define MSR_IA32_XSS 0x00000da0
#define FEATURE_CONTROL_LOCKED (1<<0)
diff --git a/arch/x86/include/asm/pat.h b/arch/x86/include/asm/pat.h
index 0b1ff4c1c14e..fffb2794dd89 100644
--- a/arch/x86/include/asm/pat.h
+++ b/arch/x86/include/asm/pat.h
@@ -7,6 +7,7 @@
bool pat_enabled(void);
void pat_disable(const char *reason);
extern void pat_init(void);
+extern void init_cache_modes(void);
extern int reserve_memtype(u64 start, u64 end,
enum page_cache_mode req_pcm, enum page_cache_mode *ret_pcm);
diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h
index 4c20dd333412..85133b2b8e99 100644
--- a/arch/x86/include/asm/xen/hypercall.h
+++ b/arch/x86/include/asm/xen/hypercall.h
@@ -43,6 +43,7 @@
#include <asm/page.h>
#include <asm/pgtable.h>
+#include <asm/smap.h>
#include <xen/interface/xen.h>
#include <xen/interface/sched.h>
@@ -213,10 +214,12 @@ privcmd_call(unsigned call,
__HYPERCALL_DECLS;
__HYPERCALL_5ARG(a1, a2, a3, a4, a5);
+ stac();
asm volatile("call *%[call]"
: __HYPERCALL_5PARAM
: [call] "a" (&hypercall_page[call])
: __HYPERCALL_CLOBBER5);
+ clac();
return (long)__res;
}
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index e75907601a41..1e5eb9f2ff5f 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -329,6 +329,14 @@ static void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger,
struct mpc_intsrc mp_irq;
/*
+ * Check bus_irq boundary.
+ */
+ if (bus_irq >= NR_IRQS_LEGACY) {
+ pr_warn("Invalid bus_irq %u for legacy override\n", bus_irq);
+ return;
+ }
+
+ /*
* Convert 'gsi' to 'ioapic.pin'.
*/
ioapic = mp_find_ioapic(gsi);
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 1e5d2f07416b..fc91c98bee01 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -2115,7 +2115,7 @@ static inline void __init check_timer(void)
int idx;
idx = find_irq_entry(apic1, pin1, mp_INT);
if (idx != -1 && irq_trigger(idx))
- unmask_ioapic_irq(irq_get_chip_data(0));
+ unmask_ioapic_irq(irq_get_irq_data(0));
}
irq_domain_deactivate_irq(irq_data);
irq_domain_activate_irq(irq_data);
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index 62aca448726a..2116176c1721 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -682,6 +682,9 @@ static int threshold_create_bank(unsigned int cpu, unsigned int bank)
const char *name = th_names[bank];
int err = 0;
+ if (!dev)
+ return -ENODEV;
+
if (is_shared_bank(bank)) {
nb = node_to_amd_nb(amd_get_nb_id(cpu));
diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
index 8900400230c6..2cdae69d7e0b 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
@@ -153,7 +153,7 @@ static void __intel_pmu_lbr_enable(bool pmi)
*/
if (cpuc->lbr_sel)
lbr_select = cpuc->lbr_sel->config;
- if (!pmi)
+ if (!pmi && cpuc->lbr_sel)
wrmsrl(MSR_LBR_SELECT, lbr_select);
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
@@ -432,8 +432,10 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc)
int out = 0;
int num = x86_pmu.lbr_nr;
- if (cpuc->lbr_sel->config & LBR_CALL_STACK)
- num = tos;
+ if (cpuc->lbr_sel) {
+ if (cpuc->lbr_sel->config & LBR_CALL_STACK)
+ num = tos;
+ }
for (i = 0; i < num; i++) {
unsigned long lbr_idx = (tos - i) & mask;
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index cec49ecf5f31..32187f8a49b4 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -151,6 +151,8 @@ void kvm_async_pf_task_wait(u32 token)
if (hlist_unhashed(&n.link))
break;
+ rcu_irq_exit();
+
if (!n.halted) {
local_irq_enable();
schedule();
@@ -159,11 +161,11 @@ void kvm_async_pf_task_wait(u32 token)
/*
* We cannot reschedule. So halt.
*/
- rcu_irq_exit();
native_safe_halt();
local_irq_disable();
- rcu_irq_enter();
}
+
+ rcu_irq_enter();
}
if (!n.halted)
finish_wait(&n.wq, &wait);
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index d2bbe343fda7..e67b834279b2 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -1048,6 +1048,13 @@ void __init setup_arch(char **cmdline_p)
if (mtrr_trim_uncached_memory(max_pfn))
max_pfn = e820_end_of_ram_pfn();
+ /*
+ * This call is required when the CPU does not support PAT. If
+ * mtrr_bp_init() invoked it already via pat_init() the call has no
+ * effect.
+ */
+ init_cache_modes();
+
#ifdef CONFIG_X86_32
/* max_low_pfn get updated here */
find_low_pfn_range();
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 9357b29de9bc..83d6369c45f5 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -46,11 +46,18 @@ static u32 xstate_required_size(u64 xstate_bv, bool compacted)
return ret;
}
+bool kvm_mpx_supported(void)
+{
+ return ((host_xcr0 & (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR))
+ && kvm_x86_ops->mpx_supported());
+}
+EXPORT_SYMBOL_GPL(kvm_mpx_supported);
+
u64 kvm_supported_xcr0(void)
{
u64 xcr0 = KVM_SUPPORTED_XCR0 & host_xcr0;
- if (!kvm_x86_ops->mpx_supported())
+ if (!kvm_mpx_supported())
xcr0 &= ~(XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);
return xcr0;
@@ -97,7 +104,7 @@ int kvm_update_cpuid(struct kvm_vcpu *vcpu)
if (best && (best->eax & (F(XSAVES) | F(XSAVEC))))
best->ebx = xstate_required_size(vcpu->arch.xcr0, true);
- vcpu->arch.eager_fpu = use_eager_fpu() || guest_cpuid_has_mpx(vcpu);
+ vcpu->arch.eager_fpu = use_eager_fpu();
if (vcpu->arch.eager_fpu)
kvm_x86_ops->fpu_activate(vcpu);
@@ -295,7 +302,7 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
#endif
unsigned f_rdtscp = kvm_x86_ops->rdtscp_supported() ? F(RDTSCP) : 0;
unsigned f_invpcid = kvm_x86_ops->invpcid_supported() ? F(INVPCID) : 0;
- unsigned f_mpx = kvm_x86_ops->mpx_supported() ? F(MPX) : 0;
+ unsigned f_mpx = kvm_mpx_supported() ? F(MPX) : 0;
unsigned f_xsaves = kvm_x86_ops->xsaves_supported() ? F(XSAVES) : 0;
/* cpuid 1.edx */
diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h
index 3f5c48ddba45..d1534feefcfe 100644
--- a/arch/x86/kvm/cpuid.h
+++ b/arch/x86/kvm/cpuid.h
@@ -4,6 +4,7 @@
#include "x86.h"
int kvm_update_cpuid(struct kvm_vcpu *vcpu);
+bool kvm_mpx_supported(void);
struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
u32 function, u32 index);
int kvm_dev_ioctl_get_cpuid(struct kvm_cpuid2 *cpuid,
@@ -134,20 +135,20 @@ static inline bool guest_cpuid_has_rtm(struct kvm_vcpu *vcpu)
return best && (best->ebx & bit(X86_FEATURE_RTM));
}
-static inline bool guest_cpuid_has_mpx(struct kvm_vcpu *vcpu)
+static inline bool guest_cpuid_has_pcommit(struct kvm_vcpu *vcpu)
{
struct kvm_cpuid_entry2 *best;
best = kvm_find_cpuid_entry(vcpu, 7, 0);
- return best && (best->ebx & bit(X86_FEATURE_MPX));
+ return best && (best->ebx & bit(X86_FEATURE_PCOMMIT));
}
-static inline bool guest_cpuid_has_pcommit(struct kvm_vcpu *vcpu)
+static inline bool guest_cpuid_has_mpx(struct kvm_vcpu *vcpu)
{
struct kvm_cpuid_entry2 *best;
best = kvm_find_cpuid_entry(vcpu, 7, 0);
- return best && (best->ebx & bit(X86_FEATURE_PCOMMIT));
+ return best && (best->ebx & bit(X86_FEATURE_MPX));
}
static inline bool guest_cpuid_has_rdtscp(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index bbaa11f4e74b..b12391119ce8 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -863,7 +863,6 @@ static unsigned long nested_ept_get_cr3(struct kvm_vcpu *vcpu);
static u64 construct_eptp(unsigned long root_hpa);
static void kvm_cpu_vmxon(u64 addr);
static void kvm_cpu_vmxoff(void);
-static bool vmx_mpx_supported(void);
static bool vmx_xsaves_supported(void);
static int vmx_cpu_uses_apicv(struct kvm_vcpu *vcpu);
static int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr);
@@ -2541,7 +2540,7 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx)
VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER |
VM_EXIT_SAVE_VMX_PREEMPTION_TIMER | VM_EXIT_ACK_INTR_ON_EXIT;
- if (vmx_mpx_supported())
+ if (kvm_mpx_supported())
vmx->nested.nested_vmx_exit_ctls_high |= VM_EXIT_CLEAR_BNDCFGS;
/* We support free control of debug control saving. */
@@ -2562,7 +2561,7 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx)
VM_ENTRY_LOAD_IA32_PAT;
vmx->nested.nested_vmx_entry_ctls_high |=
(VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR | VM_ENTRY_LOAD_IA32_EFER);
- if (vmx_mpx_supported())
+ if (kvm_mpx_supported())
vmx->nested.nested_vmx_entry_ctls_high |= VM_ENTRY_LOAD_BNDCFGS;
/* We support free control of debug control loading. */
@@ -2813,7 +2812,8 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
msr_info->data = vmcs_readl(GUEST_SYSENTER_ESP);
break;
case MSR_IA32_BNDCFGS:
- if (!vmx_mpx_supported())
+ if (!kvm_mpx_supported() ||
+ (!msr_info->host_initiated && !guest_cpuid_has_mpx(vcpu)))
return 1;
msr_info->data = vmcs_read64(GUEST_BNDCFGS);
break;
@@ -2890,7 +2890,11 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
vmcs_writel(GUEST_SYSENTER_ESP, data);
break;
case MSR_IA32_BNDCFGS:
- if (!vmx_mpx_supported())
+ if (!kvm_mpx_supported() ||
+ (!msr_info->host_initiated && !guest_cpuid_has_mpx(vcpu)))
+ return 1;
+ if (is_noncanonical_address(data & PAGE_MASK) ||
+ (data & MSR_IA32_BNDCFGS_RSVD))
return 1;
vmcs_write64(GUEST_BNDCFGS, data);
break;
@@ -3363,7 +3367,7 @@ static void init_vmcs_shadow_fields(void)
for (i = j = 0; i < max_shadow_read_write_fields; i++) {
switch (shadow_read_write_fields[i]) {
case GUEST_BNDCFGS:
- if (!vmx_mpx_supported())
+ if (!kvm_mpx_supported())
continue;
break;
default:
@@ -6253,7 +6257,6 @@ static __init int hardware_setup(void)
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_CS, false);
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_ESP, false);
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_EIP, false);
- vmx_disable_intercept_for_msr(MSR_IA32_BNDCFGS, true);
memcpy(vmx_msr_bitmap_legacy_x2apic,
vmx_msr_bitmap_legacy, PAGE_SIZE);
@@ -10265,7 +10268,7 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
vmcs12->guest_sysenter_cs = vmcs_read32(GUEST_SYSENTER_CS);
vmcs12->guest_sysenter_esp = vmcs_readl(GUEST_SYSENTER_ESP);
vmcs12->guest_sysenter_eip = vmcs_readl(GUEST_SYSENTER_EIP);
- if (vmx_mpx_supported())
+ if (kvm_mpx_supported())
vmcs12->guest_bndcfgs = vmcs_read64(GUEST_BNDCFGS);
if (nested_cpu_has_xsaves(vmcs12))
vmcs12->xss_exit_bitmap = vmcs_read64(XSS_EXIT_BITMAP);
diff --git a/arch/x86/lib/copy_user_64.S b/arch/x86/lib/copy_user_64.S
index 27f89c79a44b..423644c230e7 100644
--- a/arch/x86/lib/copy_user_64.S
+++ b/arch/x86/lib/copy_user_64.S
@@ -80,7 +80,7 @@ ENTRY(copy_user_generic_unrolled)
movl %edx,%ecx
andl $63,%edx
shrl $6,%ecx
- jz 17f
+ jz .L_copy_short_string
1: movq (%rsi),%r8
2: movq 1*8(%rsi),%r9
3: movq 2*8(%rsi),%r10
@@ -101,7 +101,8 @@ ENTRY(copy_user_generic_unrolled)
leaq 64(%rdi),%rdi
decl %ecx
jnz 1b
-17: movl %edx,%ecx
+.L_copy_short_string:
+ movl %edx,%ecx
andl $7,%edx
shrl $3,%ecx
jz 20f
@@ -215,6 +216,8 @@ ENDPROC(copy_user_generic_string)
*/
ENTRY(copy_user_enhanced_fast_string)
ASM_STAC
+ cmpl $64,%edx
+ jb .L_copy_short_string /* less then 64 bytes, avoid the costly 'rep' */
movl %edx,%ecx
1: rep
movsb
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c
index 6ad687d104ca..3f1bb4f93a5a 100644
--- a/arch/x86/mm/pat.c
+++ b/arch/x86/mm/pat.c
@@ -36,14 +36,14 @@
#undef pr_fmt
#define pr_fmt(fmt) "" fmt
-static bool boot_cpu_done;
-
-static int __read_mostly __pat_enabled = IS_ENABLED(CONFIG_X86_PAT);
-static void init_cache_modes(void);
+static bool __read_mostly boot_cpu_done;
+static bool __read_mostly pat_disabled = !IS_ENABLED(CONFIG_X86_PAT);
+static bool __read_mostly pat_initialized;
+static bool __read_mostly init_cm_done;
void pat_disable(const char *reason)
{
- if (!__pat_enabled)
+ if (pat_disabled)
return;
if (boot_cpu_done) {
@@ -51,10 +51,8 @@ void pat_disable(const char *reason)
return;
}
- __pat_enabled = 0;
+ pat_disabled = true;
pr_info("x86/PAT: %s\n", reason);
-
- init_cache_modes();
}
static int __init nopat(char *str)
@@ -66,7 +64,7 @@ early_param("nopat", nopat);
bool pat_enabled(void)
{
- return !!__pat_enabled;
+ return pat_initialized;
}
EXPORT_SYMBOL_GPL(pat_enabled);
@@ -204,6 +202,8 @@ static void __init_cache_modes(u64 pat)
update_cache_mode_entry(i, cache);
}
pr_info("x86/PAT: Configuration [0-7]: %s\n", pat_msg);
+
+ init_cm_done = true;
}
#define PAT(x, y) ((u64)PAT_ ## y << ((x)*8))
@@ -224,6 +224,7 @@ static void pat_bsp_init(u64 pat)
}
wrmsrl(MSR_IA32_CR_PAT, pat);
+ pat_initialized = true;
__init_cache_modes(pat);
}
@@ -241,10 +242,9 @@ static void pat_ap_init(u64 pat)
wrmsrl(MSR_IA32_CR_PAT, pat);
}
-static void init_cache_modes(void)
+void init_cache_modes(void)
{
u64 pat = 0;
- static int init_cm_done;
if (init_cm_done)
return;
@@ -286,8 +286,6 @@ static void init_cache_modes(void)
}
__init_cache_modes(pat);
-
- init_cm_done = 1;
}
/**
@@ -305,10 +303,8 @@ void pat_init(void)
u64 pat;
struct cpuinfo_x86 *c = &boot_cpu_data;
- if (!pat_enabled()) {
- init_cache_modes();
+ if (pat_disabled)
return;
- }
if ((c->x86_vendor == X86_VENDOR_INTEL) &&
(((c->x86 == 0x6) && (c->x86_model <= 0xd)) ||
diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index 0c2fae8d929d..73eb7fd4aec4 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -992,11 +992,12 @@ static void emit_relocs(int as_text, int use_real_mode)
die("Segment relocations found but --realmode not specified\n");
/* Order the relocations for more efficient processing */
- sort_relocs(&relocs16);
sort_relocs(&relocs32);
#if ELF_BITS == 64
sort_relocs(&relocs32neg);
sort_relocs(&relocs64);
+#else
+ sort_relocs(&relocs16);
#endif
/* Print the relocations */
diff --git a/crypto/authencesn.c b/crypto/authencesn.c
index 0c0468869e25..52154ef21b5e 100644
--- a/crypto/authencesn.c
+++ b/crypto/authencesn.c
@@ -245,6 +245,9 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req,
u8 *ihash = ohash + crypto_ahash_digestsize(auth);
u32 tmp[2];
+ if (!authsize)
+ goto decrypt;
+
/* Move high-order bits of sequence number back. */
scatterwalk_map_and_copy(tmp, dst, 4, 4, 0);
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0);
@@ -253,6 +256,8 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req,
if (crypto_memneq(ihash, ohash, authsize))
return -EBADMSG;
+decrypt:
+
sg_init_table(areq_ctx->dst, 2);
dst = scatterwalk_ffwd(areq_ctx->dst, dst, assoclen);
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index eac4f3b02df9..bb81cd05f0bc 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -1067,6 +1067,7 @@ static int ghes_remove(struct platform_device *ghes_dev)
if (list_empty(&ghes_sci))
unregister_acpi_hed_notifier(&ghes_notifier_sci);
mutex_unlock(&ghes_list_mutex);
+ synchronize_rcu();
break;
case ACPI_HEST_NOTIFY_NMI:
ghes_nmi_remove(ghes);
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 5ea5dc219f56..73c9c7fa9001 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -98,7 +98,15 @@ static int find_child_checks(struct acpi_device *adev, bool check_children)
if (check_children && list_empty(&adev->children))
return -ENODEV;
- return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
+ /*
+ * If the device has a _HID (or _CID) returning a valid ACPI/PNP
+ * device ID, it is better to make it look less attractive here, so that
+ * the other device with the same _ADR value (that may not have a valid
+ * device ID) can be matched going forward. [This means a second spec
+ * violation in a row, so whatever we do here is best effort anyway.]
+ */
+ return sta_present && list_empty(&adev->pnp.ids) ?
+ FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
}
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
diff --git a/drivers/acpi/ioapic.c b/drivers/acpi/ioapic.c
index ccdc8db16bb8..fa2cf2dc4e33 100644
--- a/drivers/acpi/ioapic.c
+++ b/drivers/acpi/ioapic.c
@@ -45,6 +45,12 @@ static acpi_status setup_res(struct acpi_resource *acpi_res, void *data)
struct resource *res = data;
struct resource_win win;
+ /*
+ * We might assign this to 'res' later, make sure all pointers are
+ * cleared before the resource is added to the global list
+ */
+ memset(&win, 0, sizeof(win));
+
res->flags = 0;
if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM) == 0)
return AE_OK;
diff --git a/drivers/android/Makefile b/drivers/android/Makefile
index 3b7e4b072c58..4b7c726bb560 100644
--- a/drivers/android/Makefile
+++ b/drivers/android/Makefile
@@ -1,3 +1,3 @@
ccflags-y += -I$(src) # needed for trace events
-obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
+obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 0c3cf182e351..13598d807de0 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -15,17 +15,49 @@
*
*/
+/*
+ * Locking overview
+ *
+ * There are 3 main spinlocks which must be acquired in the
+ * order shown:
+ *
+ * 1) proc->outer_lock : protects binder_ref
+ * binder_proc_lock() and binder_proc_unlock() are
+ * used to acq/rel.
+ * 2) node->lock : protects most fields of binder_node.
+ * binder_node_lock() and binder_node_unlock() are
+ * used to acq/rel
+ * 3) proc->inner_lock : protects the thread and node lists
+ * (proc->threads, proc->waiting_threads, proc->nodes)
+ * and all todo lists associated with the binder_proc
+ * (proc->todo, thread->todo, proc->delivered_death and
+ * node->async_todo), as well as thread->transaction_stack
+ * binder_inner_proc_lock() and binder_inner_proc_unlock()
+ * are used to acq/rel
+ *
+ * Any lock under procA must never be nested under any lock at the same
+ * level or below on procB.
+ *
+ * Functions that require a lock held on entry indicate which lock
+ * in the suffix of the function name:
+ *
+ * foo_olocked() : requires node->outer_lock
+ * foo_nlocked() : requires node->lock
+ * foo_ilocked() : requires proc->inner_lock
+ * foo_oilocked(): requires proc->outer_lock and proc->inner_lock
+ * foo_nilocked(): requires node->lock and proc->inner_lock
+ * ...
+ */
+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/cacheflush.h>
-#include <linux/atomic.h>
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
-#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/nsproxy.h>
@@ -35,23 +67,32 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
#include <linux/pid_namespace.h>
#include <linux/security.h>
+#include <linux/spinlock.h>
#ifdef CONFIG_ANDROID_BINDER_IPC_32BIT
#define BINDER_IPC_32BIT 1
#endif
#include <uapi/linux/android/binder.h>
+#include "binder_alloc.h"
#include "binder_trace.h"
+static HLIST_HEAD(binder_deferred_list);
+static DEFINE_MUTEX(binder_deferred_lock);
+
static HLIST_HEAD(binder_devices);
+static HLIST_HEAD(binder_procs);
+static DEFINE_MUTEX(binder_procs_lock);
+
+static HLIST_HEAD(binder_dead_nodes);
+static DEFINE_SPINLOCK(binder_dead_nodes_lock);
static struct dentry *binder_debugfs_dir_entry_root;
static struct dentry *binder_debugfs_dir_entry_proc;
-atomic_t binder_last_id;
+static atomic_t binder_last_id;
+static struct workqueue_struct *binder_deferred_workqueue;
#define BINDER_DEBUG_ENTRY(name) \
static int binder_##name##_open(struct inode *inode, struct file *file) \
@@ -97,17 +138,13 @@ enum {
BINDER_DEBUG_TRANSACTION_COMPLETE = 1U << 10,
BINDER_DEBUG_FREE_BUFFER = 1U << 11,
BINDER_DEBUG_INTERNAL_REFS = 1U << 12,
- BINDER_DEBUG_BUFFER_ALLOC = 1U << 13,
- BINDER_DEBUG_PRIORITY_CAP = 1U << 14,
- BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 15,
+ BINDER_DEBUG_PRIORITY_CAP = 1U << 13,
+ BINDER_DEBUG_SPINLOCKS = 1U << 14,
};
static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR |
BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION;
module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO);
-static bool binder_debug_no_lock;
-module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO);
-
static char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
module_param_named(devices, binder_devices_param, charp, S_IRUGO);
@@ -164,30 +201,27 @@ enum binder_stat_types {
};
struct binder_stats {
- int br[_IOC_NR(BR_FAILED_REPLY) + 1];
- int bc[_IOC_NR(BC_REPLY_SG) + 1];
-};
-
-/* These are still global, since it's not always easy to get the context */
-struct binder_obj_stats {
+ atomic_t br[_IOC_NR(BR_FAILED_REPLY) + 1];
+ atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1];
atomic_t obj_created[BINDER_STAT_COUNT];
atomic_t obj_deleted[BINDER_STAT_COUNT];
};
-static struct binder_obj_stats binder_obj_stats;
+static struct binder_stats binder_stats;
static inline void binder_stats_deleted(enum binder_stat_types type)
{
- atomic_inc(&binder_obj_stats.obj_deleted[type]);
+ atomic_inc(&binder_stats.obj_deleted[type]);
}
static inline void binder_stats_created(enum binder_stat_types type)
{
- atomic_inc(&binder_obj_stats.obj_created[type]);
+ atomic_inc(&binder_stats.obj_created[type]);
}
struct binder_transaction_log_entry {
int debug_id;
+ int debug_id_done;
int call_type;
int from_proc;
int from_thread;
@@ -197,48 +231,45 @@ struct binder_transaction_log_entry {
int to_node;
int data_size;
int offsets_size;
+ int return_error_line;
+ uint32_t return_error;
+ uint32_t return_error_param;
const char *context_name;
};
struct binder_transaction_log {
- int next;
- int full;
+ atomic_t cur;
+ bool full;
struct binder_transaction_log_entry entry[32];
};
+static struct binder_transaction_log binder_transaction_log;
+static struct binder_transaction_log binder_transaction_log_failed;
static struct binder_transaction_log_entry *binder_transaction_log_add(
struct binder_transaction_log *log)
{
struct binder_transaction_log_entry *e;
+ unsigned int cur = atomic_inc_return(&log->cur);
- e = &log->entry[log->next];
- memset(e, 0, sizeof(*e));
- log->next++;
- if (log->next == ARRAY_SIZE(log->entry)) {
- log->next = 0;
+ if (cur >= ARRAY_SIZE(log->entry))
log->full = 1;
- }
+ e = &log->entry[cur % ARRAY_SIZE(log->entry)];
+ WRITE_ONCE(e->debug_id_done, 0);
+ /*
+ * write-barrier to synchronize access to e->debug_id_done.
+ * We make sure the initialized 0 value is seen before
+ * memset() other fields are zeroed by memset.
+ */
+ smp_wmb();
+ memset(e, 0, sizeof(*e));
return e;
}
struct binder_context {
struct binder_node *binder_context_mgr_node;
+ struct mutex context_mgr_node_lock;
+
kuid_t binder_context_mgr_uid;
const char *name;
-
- struct mutex binder_main_lock;
- struct mutex binder_deferred_lock;
- struct mutex binder_mmap_lock;
-
- struct hlist_head binder_procs;
- struct hlist_head binder_dead_nodes;
- struct hlist_head binder_deferred_list;
-
- struct work_struct deferred_work;
- struct workqueue_struct *binder_deferred_workqueue;
- struct binder_transaction_log transaction_log;
- struct binder_transaction_log transaction_log_failed;
-
- struct binder_stats binder_stats;
};
struct binder_device {
@@ -247,11 +278,20 @@ struct binder_device {
struct binder_context context;
};
+/**
+ * struct binder_work - work enqueued on a worklist
+ * @entry: node enqueued on list
+ * @type: type of work to be performed
+ *
+ * There are separate work lists for proc, thread, and node (async).
+ */
struct binder_work {
struct list_head entry;
+
enum {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
+ BINDER_WORK_RETURN_ERROR,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
@@ -259,8 +299,76 @@ struct binder_work {
} type;
};
+struct binder_error {
+ struct binder_work work;
+ uint32_t cmd;
+};
+
+/**
+ * struct binder_node - binder node bookkeeping
+ * @debug_id: unique ID for debugging
+ * (invariant after initialized)
+ * @lock: lock for node fields
+ * @work: worklist element for node work
+ * (protected by @proc->inner_lock)
+ * @rb_node: element for proc->nodes tree
+ * (protected by @proc->inner_lock)
+ * @dead_node: element for binder_dead_nodes list
+ * (protected by binder_dead_nodes_lock)
+ * @proc: binder_proc that owns this node
+ * (invariant after initialized)
+ * @refs: list of references on this node
+ * (protected by @lock)
+ * @internal_strong_refs: used to take strong references when
+ * initiating a transaction
+ * (protected by @proc->inner_lock if @proc
+ * and by @lock)
+ * @local_weak_refs: weak user refs from local process
+ * (protected by @proc->inner_lock if @proc
+ * and by @lock)
+ * @local_strong_refs: strong user refs from local process
+ * (protected by @proc->inner_lock if @proc
+ * and by @lock)
+ * @tmp_refs: temporary kernel refs
+ * (protected by @proc->inner_lock while @proc
+ * is valid, and by binder_dead_nodes_lock
+ * if @proc is NULL. During inc/dec and node release
+ * it is also protected by @lock to provide safety
+ * as the node dies and @proc becomes NULL)
+ * @ptr: userspace pointer for node
+ * (invariant, no lock needed)
+ * @cookie: userspace cookie for node
+ * (invariant, no lock needed)
+ * @has_strong_ref: userspace notified of strong ref
+ * (protected by @proc->inner_lock if @proc
+ * and by @lock)
+ * @pending_strong_ref: userspace has acked notification of strong ref
+ * (protected by @proc->inner_lock if @proc
+ * and by @lock)
+ * @has_weak_ref: userspace notified of weak ref
+ * (protected by @proc->inner_lock if @proc
+ * and by @lock)
+ * @pending_weak_ref: userspace has acked notification of weak ref
+ * (protected by @proc->inner_lock if @proc
+ * and by @lock)
+ * @has_async_transaction: async transaction to node in progress
+ * (protected by @lock)
+ * @sched_policy: minimum scheduling policy for node
+ * (invariant after initialized)
+ * @accept_fds: file descriptor operations supported for node
+ * (invariant after initialized)
+ * @min_priority: minimum scheduling priority
+ * (invariant after initialized)
+ * @inherit_rt: inherit RT scheduling policy from caller
+ * (invariant after initialized)
+ * @async_todo: list of async work items
+ * (protected by @proc->inner_lock)
+ *
+ * Bookkeeping structure for binder nodes.
+ */
struct binder_node {
int debug_id;
+ spinlock_t lock;
struct binder_work work;
union {
struct rb_node rb_node;
@@ -271,88 +379,185 @@ struct binder_node {
int internal_strong_refs;
int local_weak_refs;
int local_strong_refs;
+ int tmp_refs;
binder_uintptr_t ptr;
binder_uintptr_t cookie;
- unsigned has_strong_ref:1;
- unsigned pending_strong_ref:1;
- unsigned has_weak_ref:1;
- unsigned pending_weak_ref:1;
- unsigned has_async_transaction:1;
- unsigned accept_fds:1;
- unsigned min_priority:8;
+ struct {
+ /*
+ * bitfield elements protected by
+ * proc inner_lock
+ */
+ u8 has_strong_ref:1;
+ u8 pending_strong_ref:1;
+ u8 has_weak_ref:1;
+ u8 pending_weak_ref:1;
+ };
+ struct {
+ /*
+ * invariant after initialization
+ */
+ u8 sched_policy:2;
+ u8 inherit_rt:1;
+ u8 accept_fds:1;
+ u8 min_priority;
+ };
+ bool has_async_transaction;
struct list_head async_todo;
};
struct binder_ref_death {
+ /**
+ * @work: worklist element for death notifications
+ * (protected by inner_lock of the proc that
+ * this ref belongs to)
+ */
struct binder_work work;
binder_uintptr_t cookie;
};
+/**
+ * struct binder_ref_data - binder_ref counts and id
+ * @debug_id: unique ID for the ref
+ * @desc: unique userspace handle for ref
+ * @strong: strong ref count (debugging only if not locked)
+ * @weak: weak ref count (debugging only if not locked)
+ *
+ * Structure to hold ref count and ref id information. Since
+ * the actual ref can only be accessed with a lock, this structure
+ * is used to return information about the ref to callers of
+ * ref inc/dec functions.
+ */
+struct binder_ref_data {
+ int debug_id;
+ uint32_t desc;
+ int strong;
+ int weak;
+};
+
+/**
+ * struct binder_ref - struct to track references on nodes
+ * @data: binder_ref_data containing id, handle, and current refcounts
+ * @rb_node_desc: node for lookup by @data.desc in proc's rb_tree
+ * @rb_node_node: node for lookup by @node in proc's rb_tree
+ * @node_entry: list entry for node->refs list in target node
+ * (protected by @node->lock)
+ * @proc: binder_proc containing ref
+ * @node: binder_node of target node. When cleaning up a
+ * ref for deletion in binder_cleanup_ref, a non-NULL
+ * @node indicates the node must be freed
+ * @death: pointer to death notification (ref_death) if requested
+ * (protected by @node->lock)
+ *
+ * Structure to track references from procA to target node (on procB). This
+ * structure is unsafe to access without holding @proc->outer_lock.
+ */
struct binder_ref {
/* Lookups needed: */
/* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */
- int debug_id;
+ struct binder_ref_data data;
struct rb_node rb_node_desc;
struct rb_node rb_node_node;
struct hlist_node node_entry;
struct binder_proc *proc;
struct binder_node *node;
- uint32_t desc;
- int strong;
- int weak;
struct binder_ref_death *death;
};
-struct binder_buffer {
- struct list_head entry; /* free and allocated entries by address */
- struct rb_node rb_node; /* free entry by size or allocated entry */
- /* by address */
- unsigned free:1;
- unsigned allow_user_free:1;
- unsigned async_transaction:1;
- unsigned debug_id:29;
-
- struct binder_transaction *transaction;
-
- struct binder_node *target_node;
- size_t data_size;
- size_t offsets_size;
- size_t extra_buffers_size;
- uint8_t data[0];
-};
-
enum binder_deferred_state {
BINDER_DEFERRED_PUT_FILES = 0x01,
BINDER_DEFERRED_FLUSH = 0x02,
BINDER_DEFERRED_RELEASE = 0x04,
};
+/**
+ * struct binder_priority - scheduler policy and priority
+ * @sched_policy scheduler policy
+ * @prio [100..139] for SCHED_NORMAL, [0..99] for FIFO/RT
+ *
+ * The binder driver supports inheriting the following scheduler policies:
+ * SCHED_NORMAL
+ * SCHED_BATCH
+ * SCHED_FIFO
+ * SCHED_RR
+ */
+struct binder_priority {
+ unsigned int sched_policy;
+ int prio;
+};
+
+/**
+ * struct binder_proc - binder process bookkeeping
+ * @proc_node: element for binder_procs list
+ * @threads: rbtree of binder_threads in this proc
+ * (protected by @inner_lock)
+ * @nodes: rbtree of binder nodes associated with
+ * this proc ordered by node->ptr
+ * (protected by @inner_lock)
+ * @refs_by_desc: rbtree of refs ordered by ref->desc
+ * (protected by @outer_lock)
+ * @refs_by_node: rbtree of refs ordered by ref->node
+ * (protected by @outer_lock)
+ * @waiting_threads: threads currently waiting for proc work
+ * (protected by @inner_lock)
+ * @pid PID of group_leader of process
+ * (invariant after initialized)
+ * @tsk task_struct for group_leader of process
+ * (invariant after initialized)
+ * @files files_struct for process
+ * (invariant after initialized)
+ * @deferred_work_node: element for binder_deferred_list
+ * (protected by binder_deferred_lock)
+ * @deferred_work: bitmap of deferred work to perform
+ * (protected by binder_deferred_lock)
+ * @is_dead: process is dead and awaiting free
+ * when outstanding transactions are cleaned up
+ * (protected by @inner_lock)
+ * @todo: list of work for this process
+ * (protected by @inner_lock)
+ * @wait: wait queue head to wait for proc work
+ * (invariant after initialized)
+ * @stats: per-process binder statistics
+ * (atomics, no lock needed)
+ * @delivered_death: list of delivered death notification
+ * (protected by @inner_lock)
+ * @max_threads: cap on number of binder threads
+ * (protected by @inner_lock)
+ * @requested_threads: number of binder threads requested but not
+ * yet started. In current implementation, can
+ * only be 0 or 1.
+ * (protected by @inner_lock)
+ * @requested_threads_started: number binder threads started
+ * (protected by @inner_lock)
+ * @tmp_ref: temporary reference to indicate proc is in use
+ * (protected by @inner_lock)
+ * @default_priority: default scheduler priority
+ * (invariant after initialized)
+ * @debugfs_entry: debugfs node
+ * @alloc: binder allocator bookkeeping
+ * @context: binder_context for this proc
+ * (invariant after initialized)
+ * @inner_lock: can nest under outer_lock and/or node lock
+ * @outer_lock: no nesting under innor or node lock
+ * Lock order: 1) outer, 2) node, 3) inner
+ *
+ * Bookkeeping structure for binder processes
+ */
struct binder_proc {
struct hlist_node proc_node;
struct rb_root threads;
struct rb_root nodes;
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
+ struct list_head waiting_threads;
int pid;
- struct vm_area_struct *vma;
- struct mm_struct *vma_vm_mm;
struct task_struct *tsk;
struct files_struct *files;
struct hlist_node deferred_work_node;
int deferred_work;
- void *buffer;
- ptrdiff_t user_buffer_offset;
-
- struct list_head buffers;
- struct rb_root free_buffers;
- struct rb_root allocated_buffers;
- size_t free_async_space;
+ bool is_dead;
- struct page **pages;
- size_t buffer_size;
- uint32_t buffer_free;
struct list_head todo;
wait_queue_head_t wait;
struct binder_stats stats;
@@ -360,10 +565,13 @@ struct binder_proc {
int max_threads;
int requested_threads;
int requested_threads_started;
- int ready_threads;
- long default_priority;
+ int tmp_ref;
+ struct binder_priority default_priority;
struct dentry *debugfs_entry;
+ struct binder_alloc alloc;
struct binder_context *context;
+ spinlock_t inner_lock;
+ spinlock_t outer_lock;
};
enum {
@@ -372,22 +580,60 @@ enum {
BINDER_LOOPER_STATE_EXITED = 0x04,
BINDER_LOOPER_STATE_INVALID = 0x08,
BINDER_LOOPER_STATE_WAITING = 0x10,
- BINDER_LOOPER_STATE_NEED_RETURN = 0x20
+ BINDER_LOOPER_STATE_POLL = 0x20,
};
+/**
+ * struct binder_thread - binder thread bookkeeping
+ * @proc: binder process for this thread
+ * (invariant after initialization)
+ * @rb_node: element for proc->threads rbtree
+ * (protected by @proc->inner_lock)
+ * @waiting_thread_node: element for @proc->waiting_threads list
+ * (protected by @proc->inner_lock)
+ * @pid: PID for this thread
+ * (invariant after initialization)
+ * @looper: bitmap of looping state
+ * (only accessed by this thread)
+ * @looper_needs_return: looping thread needs to exit driver
+ * (no lock needed)
+ * @transaction_stack: stack of in-progress transactions for this thread
+ * (protected by @proc->inner_lock)
+ * @todo: list of work to do for this thread
+ * (protected by @proc->inner_lock)
+ * @return_error: transaction errors reported by this thread
+ * (only accessed by this thread)
+ * @reply_error: transaction errors reported by target thread
+ * (protected by @proc->inner_lock)
+ * @wait: wait queue for thread work
+ * @stats: per-thread statistics
+ * (atomics, no lock needed)
+ * @tmp_ref: temporary reference to indicate thread is in use
+ * (atomic since @proc->inner_lock cannot
+ * always be acquired)
+ * @is_dead: thread is dead and awaiting free
+ * when outstanding transactions are cleaned up
+ * (protected by @proc->inner_lock)
+ * @task: struct task_struct for this thread
+ *
+ * Bookkeeping structure for binder threads.
+ */
struct binder_thread {
struct binder_proc *proc;
struct rb_node rb_node;
+ struct list_head waiting_thread_node;
int pid;
- int looper;
+ int looper; /* only modified by this thread */
+ bool looper_need_return; /* can be written by other thread */
struct binder_transaction *transaction_stack;
struct list_head todo;
- uint32_t return_error; /* Write failed, return error code in read buf */
- uint32_t return_error2; /* Write failed, return error code in read */
- /* buffer. Used when sending a reply to a dead process that */
- /* we are also waiting on */
+ struct binder_error return_error;
+ struct binder_error reply_error;
wait_queue_head_t wait;
struct binder_stats stats;
+ atomic_t tmp_ref;
+ bool is_dead;
+ struct task_struct *task;
};
struct binder_transaction {
@@ -404,20 +650,263 @@ struct binder_transaction {
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
- long priority;
- long saved_priority;
+ struct binder_priority priority;
+ struct binder_priority saved_priority;
+ bool set_priority_called;
kuid_t sender_euid;
+ /**
+ * @lock: protects @from, @to_proc, and @to_thread
+ *
+ * @from, @to_proc, and @to_thread can be set to NULL
+ * during thread teardown
+ */
+ spinlock_t lock;
};
+/**
+ * binder_proc_lock() - Acquire outer lock for given binder_proc
+ * @proc: struct binder_proc to acquire
+ *
+ * Acquires proc->outer_lock. Used to protect binder_ref
+ * structures associated with the given proc.
+ */
+#define binder_proc_lock(proc) _binder_proc_lock(proc, __LINE__)
+static void
+_binder_proc_lock(struct binder_proc *proc, int line)
+{
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ spin_lock(&proc->outer_lock);
+}
+
+/**
+ * binder_proc_unlock() - Release spinlock for given binder_proc
+ * @proc: struct binder_proc to acquire
+ *
+ * Release lock acquired via binder_proc_lock()
+ */
+#define binder_proc_unlock(_proc) _binder_proc_unlock(_proc, __LINE__)
+static void
+_binder_proc_unlock(struct binder_proc *proc, int line)
+{
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ spin_unlock(&proc->outer_lock);
+}
+
+/**
+ * binder_inner_proc_lock() - Acquire inner lock for given binder_proc
+ * @proc: struct binder_proc to acquire
+ *
+ * Acquires proc->inner_lock. Used to protect todo lists
+ */
+#define binder_inner_proc_lock(proc) _binder_inner_proc_lock(proc, __LINE__)
+static void
+_binder_inner_proc_lock(struct binder_proc *proc, int line)
+{
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ spin_lock(&proc->inner_lock);
+}
+
+/**
+ * binder_inner_proc_unlock() - Release inner lock for given binder_proc
+ * @proc: struct binder_proc to acquire
+ *
+ * Release lock acquired via binder_inner_proc_lock()
+ */
+#define binder_inner_proc_unlock(proc) _binder_inner_proc_unlock(proc, __LINE__)
+static void
+_binder_inner_proc_unlock(struct binder_proc *proc, int line)
+{
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ spin_unlock(&proc->inner_lock);
+}
+
+/**
+ * binder_node_lock() - Acquire spinlock for given binder_node
+ * @node: struct binder_node to acquire
+ *
+ * Acquires node->lock. Used to protect binder_node fields
+ */
+#define binder_node_lock(node) _binder_node_lock(node, __LINE__)
+static void
+_binder_node_lock(struct binder_node *node, int line)
+{
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ spin_lock(&node->lock);
+}
+
+/**
+ * binder_node_unlock() - Release spinlock for given binder_proc
+ * @node: struct binder_node to acquire
+ *
+ * Release lock acquired via binder_node_lock()
+ */
+#define binder_node_unlock(node) _binder_node_unlock(node, __LINE__)
+static void
+_binder_node_unlock(struct binder_node *node, int line)
+{
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ spin_unlock(&node->lock);
+}
+
+/**
+ * binder_node_inner_lock() - Acquire node and inner locks
+ * @node: struct binder_node to acquire
+ *
+ * Acquires node->lock. If node->proc also acquires
+ * proc->inner_lock. Used to protect binder_node fields
+ */
+#define binder_node_inner_lock(node) _binder_node_inner_lock(node, __LINE__)
+static void
+_binder_node_inner_lock(struct binder_node *node, int line)
+{
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ spin_lock(&node->lock);
+ if (node->proc)
+ binder_inner_proc_lock(node->proc);
+}
+
+/**
+ * binder_node_unlock() - Release node and inner locks
+ * @node: struct binder_node to acquire
+ *
+ * Release lock acquired via binder_node_lock()
+ */
+#define binder_node_inner_unlock(node) _binder_node_inner_unlock(node, __LINE__)
+static void
+_binder_node_inner_unlock(struct binder_node *node, int line)
+{
+ struct binder_proc *proc = node->proc;
+
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ if (proc)
+ binder_inner_proc_unlock(proc);
+ spin_unlock(&node->lock);
+}
+
+static bool binder_worklist_empty_ilocked(struct list_head *list)
+{
+ return list_empty(list);
+}
+
+/**
+ * binder_worklist_empty() - Check if no items on the work list
+ * @proc: binder_proc associated with list
+ * @list: list to check
+ *
+ * Return: true if there are no items on list, else false
+ */
+static bool binder_worklist_empty(struct binder_proc *proc,
+ struct list_head *list)
+{
+ bool ret;
+
+ binder_inner_proc_lock(proc);
+ ret = binder_worklist_empty_ilocked(list);
+ binder_inner_proc_unlock(proc);
+ return ret;
+}
+
+static void
+binder_enqueue_work_ilocked(struct binder_work *work,
+ struct list_head *target_list)
+{
+ BUG_ON(target_list == NULL);
+ BUG_ON(work->entry.next && !list_empty(&work->entry));
+ list_add_tail(&work->entry, target_list);
+}
+
+/**
+ * binder_enqueue_work() - Add an item to the work list
+ * @proc: binder_proc associated with list
+ * @work: struct binder_work to add to list
+ * @target_list: list to add work to
+ *
+ * Adds the work to the specified list. Asserts that work
+ * is not already on a list.
+ */
+static void
+binder_enqueue_work(struct binder_proc *proc,
+ struct binder_work *work,
+ struct list_head *target_list)
+{
+ binder_inner_proc_lock(proc);
+ binder_enqueue_work_ilocked(work, target_list);
+ binder_inner_proc_unlock(proc);
+}
+
+static void
+binder_dequeue_work_ilocked(struct binder_work *work)
+{
+ list_del_init(&work->entry);
+}
+
+/**
+ * binder_dequeue_work() - Removes an item from the work list
+ * @proc: binder_proc associated with list
+ * @work: struct binder_work to remove from list
+ *
+ * Removes the specified work item from whatever list it is on.
+ * Can safely be called if work is not on any list.
+ */
+static void
+binder_dequeue_work(struct binder_proc *proc, struct binder_work *work)
+{
+ binder_inner_proc_lock(proc);
+ binder_dequeue_work_ilocked(work);
+ binder_inner_proc_unlock(proc);
+}
+
+static struct binder_work *binder_dequeue_work_head_ilocked(
+ struct list_head *list)
+{
+ struct binder_work *w;
+
+ w = list_first_entry_or_null(list, struct binder_work, entry);
+ if (w)
+ list_del_init(&w->entry);
+ return w;
+}
+
+/**
+ * binder_dequeue_work_head() - Dequeues the item at head of list
+ * @proc: binder_proc associated with list
+ * @list: list to dequeue head
+ *
+ * Removes the head of the list if there are items on the list
+ *
+ * Return: pointer dequeued binder_work, NULL if list was empty
+ */
+static struct binder_work *binder_dequeue_work_head(
+ struct binder_proc *proc,
+ struct list_head *list)
+{
+ struct binder_work *w;
+
+ binder_inner_proc_lock(proc);
+ w = binder_dequeue_work_head_ilocked(list);
+ binder_inner_proc_unlock(proc);
+ return w;
+}
+
static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer);
+static void binder_free_thread(struct binder_thread *thread);
+static void binder_free_proc(struct binder_proc *proc);
+static void binder_inc_node_tmpref_ilocked(struct binder_node *node);
static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
{
struct files_struct *files = proc->files;
unsigned long rlim_cur;
unsigned long irqs;
- int ret;
if (files == NULL)
return -ESRCH;
@@ -428,11 +917,7 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE);
unlock_task_sighand(proc->tsk, &irqs);
- preempt_enable_no_resched();
- ret = __alloc_fd(files, 0, rlim_cur, flags);
- preempt_disable();
-
- return ret;
+ return __alloc_fd(files, 0, rlim_cur, flags);
}
/*
@@ -441,11 +926,8 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
static void task_fd_install(
struct binder_proc *proc, unsigned int fd, struct file *file)
{
- if (proc->files) {
- preempt_enable_no_resched();
+ if (proc->files)
__fd_install(proc->files, fd, file);
- preempt_disable();
- }
}
/*
@@ -469,526 +951,281 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd)
return retval;
}
-static inline void binder_lock(struct binder_context *context, const char *tag)
-{
- trace_binder_lock(tag);
- mutex_lock(&context->binder_main_lock);
- preempt_disable();
- trace_binder_locked(tag);
-}
-
-static inline void binder_unlock(struct binder_context *context,
- const char *tag)
+static bool binder_has_work_ilocked(struct binder_thread *thread,
+ bool do_proc_work)
{
- trace_binder_unlock(tag);
- mutex_unlock(&context->binder_main_lock);
- preempt_enable();
+ return !binder_worklist_empty_ilocked(&thread->todo) ||
+ thread->looper_need_return ||
+ (do_proc_work &&
+ !binder_worklist_empty_ilocked(&thread->proc->todo));
}
-static inline void *kzalloc_preempt_disabled(size_t size)
+static bool binder_has_work(struct binder_thread *thread, bool do_proc_work)
{
- void *ptr;
+ bool has_work;
- ptr = kzalloc(size, GFP_NOWAIT);
- if (ptr)
- return ptr;
+ binder_inner_proc_lock(thread->proc);
+ has_work = binder_has_work_ilocked(thread, do_proc_work);
+ binder_inner_proc_unlock(thread->proc);
- preempt_enable_no_resched();
- ptr = kzalloc(size, GFP_KERNEL);
- preempt_disable();
-
- return ptr;
-}
-
-static inline long copy_to_user_preempt_disabled(void __user *to, const void *from, long n)
-{
- long ret;
-
- preempt_enable_no_resched();
- ret = copy_to_user(to, from, n);
- preempt_disable();
- return ret;
+ return has_work;
}
-static inline long copy_from_user_preempt_disabled(void *to, const void __user *from, long n)
+static bool binder_available_for_proc_work_ilocked(struct binder_thread *thread)
{
- long ret;
-
- preempt_enable_no_resched();
- ret = copy_from_user(to, from, n);
- preempt_disable();
- return ret;
+ return !thread->transaction_stack &&
+ binder_worklist_empty_ilocked(&thread->todo) &&
+ (thread->looper & (BINDER_LOOPER_STATE_ENTERED |
+ BINDER_LOOPER_STATE_REGISTERED));
}
-#define get_user_preempt_disabled(x, ptr) \
-({ \
- int __ret; \
- preempt_enable_no_resched(); \
- __ret = get_user(x, ptr); \
- preempt_disable(); \
- __ret; \
-})
-
-#define put_user_preempt_disabled(x, ptr) \
-({ \
- int __ret; \
- preempt_enable_no_resched(); \
- __ret = put_user(x, ptr); \
- preempt_disable(); \
- __ret; \
-})
-
-static void binder_set_nice(long nice)
+static void binder_wakeup_poll_threads_ilocked(struct binder_proc *proc,
+ bool sync)
{
- long min_nice;
+ struct rb_node *n;
+ struct binder_thread *thread;
- if (can_nice(current, nice)) {
- set_user_nice(current, nice);
- return;
+ for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) {
+ thread = rb_entry(n, struct binder_thread, rb_node);
+ if (thread->looper & BINDER_LOOPER_STATE_POLL &&
+ binder_available_for_proc_work_ilocked(thread)) {
+ if (sync)
+ wake_up_interruptible_sync(&thread->wait);
+ else
+ wake_up_interruptible(&thread->wait);
+ }
}
- min_nice = rlimit_to_nice(current->signal->rlim[RLIMIT_NICE].rlim_cur);
- binder_debug(BINDER_DEBUG_PRIORITY_CAP,
- "%d: nice value %ld not allowed use %ld instead\n",
- current->pid, nice, min_nice);
- set_user_nice(current, min_nice);
- if (min_nice <= MAX_NICE)
- return;
- binder_user_error("%d RLIMIT_NICE not set\n", current->pid);
}
-static size_t binder_buffer_size(struct binder_proc *proc,
- struct binder_buffer *buffer)
-{
- if (list_is_last(&buffer->entry, &proc->buffers))
- return proc->buffer + proc->buffer_size - (void *)buffer->data;
- return (size_t)list_entry(buffer->entry.next,
- struct binder_buffer, entry) - (size_t)buffer->data;
-}
-
-static void binder_insert_free_buffer(struct binder_proc *proc,
- struct binder_buffer *new_buffer)
+/**
+ * binder_select_thread_ilocked() - selects a thread for doing proc work.
+ * @proc: process to select a thread from
+ *
+ * Note that calling this function moves the thread off the waiting_threads
+ * list, so it can only be woken up by the caller of this function, or a
+ * signal. Therefore, callers *should* always wake up the thread this function
+ * returns.
+ *
+ * Return: If there's a thread currently waiting for process work,
+ * returns that thread. Otherwise returns NULL.
+ */
+static struct binder_thread *
+binder_select_thread_ilocked(struct binder_proc *proc)
{
- struct rb_node **p = &proc->free_buffers.rb_node;
- struct rb_node *parent = NULL;
- struct binder_buffer *buffer;
- size_t buffer_size;
- size_t new_buffer_size;
-
- BUG_ON(!new_buffer->free);
-
- new_buffer_size = binder_buffer_size(proc, new_buffer);
-
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: add free buffer, size %zd, at %p\n",
- proc->pid, new_buffer_size, new_buffer);
+ struct binder_thread *thread;
- while (*p) {
- parent = *p;
- buffer = rb_entry(parent, struct binder_buffer, rb_node);
- BUG_ON(!buffer->free);
+ assert_spin_locked(&proc->inner_lock);
+ thread = list_first_entry_or_null(&proc->waiting_threads,
+ struct binder_thread,
+ waiting_thread_node);
- buffer_size = binder_buffer_size(proc, buffer);
+ if (thread)
+ list_del_init(&thread->waiting_thread_node);
- if (new_buffer_size < buffer_size)
- p = &parent->rb_left;
- else
- p = &parent->rb_right;
- }
- rb_link_node(&new_buffer->rb_node, parent, p);
- rb_insert_color(&new_buffer->rb_node, &proc->free_buffers);
+ return thread;
}
-static void binder_insert_allocated_buffer(struct binder_proc *proc,
- struct binder_buffer *new_buffer)
+/**
+ * binder_wakeup_thread_ilocked() - wakes up a thread for doing proc work.
+ * @proc: process to wake up a thread in
+ * @thread: specific thread to wake-up (may be NULL)
+ * @sync: whether to do a synchronous wake-up
+ *
+ * This function wakes up a thread in the @proc process.
+ * The caller may provide a specific thread to wake-up in
+ * the @thread parameter. If @thread is NULL, this function
+ * will wake up threads that have called poll().
+ *
+ * Note that for this function to work as expected, callers
+ * should first call binder_select_thread() to find a thread
+ * to handle the work (if they don't have a thread already),
+ * and pass the result into the @thread parameter.
+ */
+static void binder_wakeup_thread_ilocked(struct binder_proc *proc,
+ struct binder_thread *thread,
+ bool sync)
{
- struct rb_node **p = &proc->allocated_buffers.rb_node;
- struct rb_node *parent = NULL;
- struct binder_buffer *buffer;
-
- BUG_ON(new_buffer->free);
-
- while (*p) {
- parent = *p;
- buffer = rb_entry(parent, struct binder_buffer, rb_node);
- BUG_ON(buffer->free);
+ assert_spin_locked(&proc->inner_lock);
- if (new_buffer < buffer)
- p = &parent->rb_left;
- else if (new_buffer > buffer)
- p = &parent->rb_right;
+ if (thread) {
+ if (sync)
+ wake_up_interruptible_sync(&thread->wait);
else
- BUG();
+ wake_up_interruptible(&thread->wait);
+ return;
}
- rb_link_node(&new_buffer->rb_node, parent, p);
- rb_insert_color(&new_buffer->rb_node, &proc->allocated_buffers);
+
+ /* Didn't find a thread waiting for proc work; this can happen
+ * in two scenarios:
+ * 1. All threads are busy handling transactions
+ * In that case, one of those threads should call back into
+ * the kernel driver soon and pick up this work.
+ * 2. Threads are using the (e)poll interface, in which case
+ * they may be blocked on the waitqueue without having been
+ * added to waiting_threads. For this case, we just iterate
+ * over all threads not handling transaction work, and
+ * wake them all up. We wake all because we don't know whether
+ * a thread that called into (e)poll is handling non-binder
+ * work currently.
+ */
+ binder_wakeup_poll_threads_ilocked(proc, sync);
}
-static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc,
- uintptr_t user_ptr)
+static void binder_wakeup_proc_ilocked(struct binder_proc *proc)
{
- struct rb_node *n = proc->allocated_buffers.rb_node;
- struct binder_buffer *buffer;
- struct binder_buffer *kern_ptr;
-
- kern_ptr = (struct binder_buffer *)(user_ptr - proc->user_buffer_offset
- - offsetof(struct binder_buffer, data));
+ struct binder_thread *thread = binder_select_thread_ilocked(proc);
- while (n) {
- buffer = rb_entry(n, struct binder_buffer, rb_node);
- BUG_ON(buffer->free);
-
- if (kern_ptr < buffer)
- n = n->rb_left;
- else if (kern_ptr > buffer)
- n = n->rb_right;
- else
- return buffer;
- }
- return NULL;
+ binder_wakeup_thread_ilocked(proc, thread, /* sync = */false);
}
-static int binder_update_page_range(struct binder_proc *proc, int allocate,
- void *start, void *end,
- struct vm_area_struct *vma)
+static bool is_rt_policy(int policy)
{
- void *page_addr;
- unsigned long user_page_addr;
- struct page **page;
- struct mm_struct *mm;
-
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: %s pages %p-%p\n", proc->pid,
- allocate ? "allocate" : "free", start, end);
+ return policy == SCHED_FIFO || policy == SCHED_RR;
+}
- if (end <= start)
- return 0;
+static bool is_fair_policy(int policy)
+{
+ return policy == SCHED_NORMAL || policy == SCHED_BATCH;
+}
- trace_binder_update_page_range(proc, allocate, start, end);
+static bool binder_supported_policy(int policy)
+{
+ return is_fair_policy(policy) || is_rt_policy(policy);
+}
- if (vma)
- mm = NULL;
+static int to_userspace_prio(int policy, int kernel_priority)
+{
+ if (is_fair_policy(policy))
+ return PRIO_TO_NICE(kernel_priority);
else
- mm = get_task_mm(proc->tsk);
+ return MAX_USER_RT_PRIO - 1 - kernel_priority;
+}
- preempt_enable_no_resched();
+static int to_kernel_prio(int policy, int user_priority)
+{
+ if (is_fair_policy(policy))
+ return NICE_TO_PRIO(user_priority);
+ else
+ return MAX_USER_RT_PRIO - 1 - user_priority;
+}
- if (mm) {
- down_write(&mm->mmap_sem);
- vma = proc->vma;
- if (vma && mm != proc->vma_vm_mm) {
- pr_err("%d: vma mm and task mm mismatch\n",
- proc->pid);
- vma = NULL;
- }
- }
+static void binder_do_set_priority(struct task_struct *task,
+ struct binder_priority desired,
+ bool verify)
+{
+ int priority; /* user-space prio value */
+ bool has_cap_nice;
+ unsigned int policy = desired.sched_policy;
- if (allocate == 0)
- goto free_range;
+ if (task->policy == policy && task->normal_prio == desired.prio)
+ return;
- if (vma == NULL) {
- pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
- proc->pid);
- goto err_no_vma;
- }
+ has_cap_nice = has_capability_noaudit(task, CAP_SYS_NICE);
- for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
- int ret;
+ priority = to_userspace_prio(policy, desired.prio);
- page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
+ if (verify && is_rt_policy(policy) && !has_cap_nice) {
+ long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO);
- BUG_ON(*page);
- *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
- if (*page == NULL) {
- pr_err("%d: binder_alloc_buf failed for page at %p\n",
- proc->pid, page_addr);
- goto err_alloc_page_failed;
+ if (max_rtprio == 0) {
+ policy = SCHED_NORMAL;
+ priority = MIN_NICE;
+ } else if (priority > max_rtprio) {
+ priority = max_rtprio;
}
- ret = map_kernel_range_noflush((unsigned long)page_addr,
- PAGE_SIZE, PAGE_KERNEL, page);
- flush_cache_vmap((unsigned long)page_addr,
- (unsigned long)page_addr + PAGE_SIZE);
- if (ret != 1) {
- pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
- proc->pid, page_addr);
- goto err_map_kernel_failed;
- }
- user_page_addr =
- (uintptr_t)page_addr + proc->user_buffer_offset;
- ret = vm_insert_page(vma, user_page_addr, page[0]);
- if (ret) {
- pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
- proc->pid, user_page_addr);
- goto err_vm_insert_page_failed;
- }
- /* vm_insert_page does not seem to increment the refcount */
- }
- if (mm) {
- up_write(&mm->mmap_sem);
- mmput(mm);
}
- preempt_disable();
+ if (verify && is_fair_policy(policy) && !has_cap_nice) {
+ long min_nice = rlimit_to_nice(task_rlimit(task, RLIMIT_NICE));
- return 0;
-
-free_range:
- for (page_addr = end - PAGE_SIZE; page_addr >= start;
- page_addr -= PAGE_SIZE) {
- page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
- if (vma)
- zap_page_range(vma, (uintptr_t)page_addr +
- proc->user_buffer_offset, PAGE_SIZE, NULL);
-err_vm_insert_page_failed:
- unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
-err_map_kernel_failed:
- __free_page(*page);
- *page = NULL;
-err_alloc_page_failed:
- ;
- }
-err_no_vma:
- if (mm) {
- up_write(&mm->mmap_sem);
- mmput(mm);
- }
-
- preempt_disable();
-
- return -ENOMEM;
-}
-
-static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc,
- size_t data_size,
- size_t offsets_size,
- size_t extra_buffers_size,
- int is_async)
-{
- struct rb_node *n = proc->free_buffers.rb_node;
- struct binder_buffer *buffer;
- size_t buffer_size;
- struct rb_node *best_fit = NULL;
- void *has_page_addr;
- void *end_page_addr;
- size_t size, data_offsets_size;
-
- if (proc->vma == NULL) {
- pr_err("%d: binder_alloc_buf, no vma\n",
- proc->pid);
- return NULL;
- }
-
- data_offsets_size = ALIGN(data_size, sizeof(void *)) +
- ALIGN(offsets_size, sizeof(void *));
-
- if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
- binder_user_error("%d: got transaction with invalid size %zd-%zd\n",
- proc->pid, data_size, offsets_size);
- return NULL;
- }
- size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
- if (size < data_offsets_size || size < extra_buffers_size) {
- binder_user_error("%d: got transaction with invalid extra_buffers_size %zd\n",
- proc->pid, extra_buffers_size);
- return NULL;
- }
- if (is_async &&
- proc->free_async_space < size + sizeof(struct binder_buffer)) {
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_alloc_buf size %zd failed, no async space left\n",
- proc->pid, size);
- return NULL;
- }
-
- while (n) {
- buffer = rb_entry(n, struct binder_buffer, rb_node);
- BUG_ON(!buffer->free);
- buffer_size = binder_buffer_size(proc, buffer);
-
- if (size < buffer_size) {
- best_fit = n;
- n = n->rb_left;
- } else if (size > buffer_size)
- n = n->rb_right;
- else {
- best_fit = n;
- break;
+ if (min_nice > MAX_NICE) {
+ binder_user_error("%d RLIMIT_NICE not set\n",
+ task->pid);
+ return;
+ } else if (priority < min_nice) {
+ priority = min_nice;
}
}
- if (best_fit == NULL) {
- pr_err("%d: binder_alloc_buf size %zd failed, no address space\n",
- proc->pid, size);
- return NULL;
- }
- if (n == NULL) {
- buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
- buffer_size = binder_buffer_size(proc, buffer);
- }
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_alloc_buf size %zd got buffer %p size %zd\n",
- proc->pid, size, buffer, buffer_size);
+ if (policy != desired.sched_policy ||
+ to_kernel_prio(policy, priority) != desired.prio)
+ binder_debug(BINDER_DEBUG_PRIORITY_CAP,
+ "%d: priority %d not allowed, using %d instead\n",
+ task->pid, desired.prio,
+ to_kernel_prio(policy, priority));
- has_page_addr =
- (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK);
- if (n == NULL) {
- if (size + sizeof(struct binder_buffer) + 4 >= buffer_size)
- buffer_size = size; /* no room for other buffers */
- else
- buffer_size = size + sizeof(struct binder_buffer);
- }
- end_page_addr =
- (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
- if (end_page_addr > has_page_addr)
- end_page_addr = has_page_addr;
- if (binder_update_page_range(proc, 1,
- (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL))
- return NULL;
+ /* Set the actual priority */
+ if (task->policy != policy || is_rt_policy(policy)) {
+ struct sched_param params;
- rb_erase(best_fit, &proc->free_buffers);
- buffer->free = 0;
- binder_insert_allocated_buffer(proc, buffer);
- if (buffer_size != size) {
- struct binder_buffer *new_buffer = (void *)buffer->data + size;
+ params.sched_priority = is_rt_policy(policy) ? priority : 0;
- list_add(&new_buffer->entry, &buffer->entry);
- new_buffer->free = 1;
- binder_insert_free_buffer(proc, new_buffer);
+ sched_setscheduler_nocheck(task,
+ policy | SCHED_RESET_ON_FORK,
+ &params);
}
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_alloc_buf size %zd got %p\n",
- proc->pid, size, buffer);
- buffer->data_size = data_size;
- buffer->offsets_size = offsets_size;
- buffer->extra_buffers_size = extra_buffers_size;
- buffer->async_transaction = is_async;
- if (is_async) {
- proc->free_async_space -= size + sizeof(struct binder_buffer);
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
- "%d: binder_alloc_buf size %zd async free %zd\n",
- proc->pid, size, proc->free_async_space);
- }
-
- return buffer;
-}
-
-static void *buffer_start_page(struct binder_buffer *buffer)
-{
- return (void *)((uintptr_t)buffer & PAGE_MASK);
+ if (is_fair_policy(policy))
+ set_user_nice(task, priority);
}
-static void *buffer_end_page(struct binder_buffer *buffer)
+static void binder_set_priority(struct task_struct *task,
+ struct binder_priority desired)
{
- return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK);
+ binder_do_set_priority(task, desired, /* verify = */ true);
}
-static void binder_delete_free_buffer(struct binder_proc *proc,
- struct binder_buffer *buffer)
+static void binder_restore_priority(struct task_struct *task,
+ struct binder_priority desired)
{
- struct binder_buffer *prev, *next = NULL;
- int free_page_end = 1;
- int free_page_start = 1;
-
- BUG_ON(proc->buffers.next == &buffer->entry);
- prev = list_entry(buffer->entry.prev, struct binder_buffer, entry);
- BUG_ON(!prev->free);
- if (buffer_end_page(prev) == buffer_start_page(buffer)) {
- free_page_start = 0;
- if (buffer_end_page(prev) == buffer_end_page(buffer))
- free_page_end = 0;
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %p share page with %p\n",
- proc->pid, buffer, prev);
- }
-
- if (!list_is_last(&buffer->entry, &proc->buffers)) {
- next = list_entry(buffer->entry.next,
- struct binder_buffer, entry);
- if (buffer_start_page(next) == buffer_end_page(buffer)) {
- free_page_end = 0;
- if (buffer_start_page(next) ==
- buffer_start_page(buffer))
- free_page_start = 0;
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %p share page with %p\n",
- proc->pid, buffer, prev);
- }
- }
- list_del(&buffer->entry);
- if (free_page_start || free_page_end) {
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %p do not share page%s%s with %p or %p\n",
- proc->pid, buffer, free_page_start ? "" : " end",
- free_page_end ? "" : " start", prev, next);
- binder_update_page_range(proc, 0, free_page_start ?
- buffer_start_page(buffer) : buffer_end_page(buffer),
- (free_page_end ? buffer_end_page(buffer) :
- buffer_start_page(buffer)) + PAGE_SIZE, NULL);
- }
+ binder_do_set_priority(task, desired, /* verify = */ false);
}
-static void binder_free_buf(struct binder_proc *proc,
- struct binder_buffer *buffer)
+static void binder_transaction_priority(struct task_struct *task,
+ struct binder_transaction *t,
+ struct binder_priority node_prio,
+ bool inherit_rt)
{
- size_t size, buffer_size;
-
- buffer_size = binder_buffer_size(proc, buffer);
-
- size = ALIGN(buffer->data_size, sizeof(void *)) +
- ALIGN(buffer->offsets_size, sizeof(void *)) +
- ALIGN(buffer->extra_buffers_size, sizeof(void *));
+ struct binder_priority desired_prio;
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_free_buf %p size %zd buffer_size %zd\n",
- proc->pid, buffer, size, buffer_size);
-
- BUG_ON(buffer->free);
- BUG_ON(size > buffer_size);
- BUG_ON(buffer->transaction != NULL);
- BUG_ON((void *)buffer < proc->buffer);
- BUG_ON((void *)buffer > proc->buffer + proc->buffer_size);
+ if (t->set_priority_called)
+ return;
- if (buffer->async_transaction) {
- proc->free_async_space += size + sizeof(struct binder_buffer);
+ t->set_priority_called = true;
+ t->saved_priority.sched_policy = task->policy;
+ t->saved_priority.prio = task->normal_prio;
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
- "%d: binder_free_buf size %zd async free %zd\n",
- proc->pid, size, proc->free_async_space);
+ if (!inherit_rt && is_rt_policy(desired_prio.sched_policy)) {
+ desired_prio.prio = NICE_TO_PRIO(0);
+ desired_prio.sched_policy = SCHED_NORMAL;
+ } else {
+ desired_prio.prio = t->priority.prio;
+ desired_prio.sched_policy = t->priority.sched_policy;
}
- binder_update_page_range(proc, 0,
- (void *)PAGE_ALIGN((uintptr_t)buffer->data),
- (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
- NULL);
- rb_erase(&buffer->rb_node, &proc->allocated_buffers);
- buffer->free = 1;
- if (!list_is_last(&buffer->entry, &proc->buffers)) {
- struct binder_buffer *next = list_entry(buffer->entry.next,
- struct binder_buffer, entry);
-
- if (next->free) {
- rb_erase(&next->rb_node, &proc->free_buffers);
- binder_delete_free_buffer(proc, next);
- }
- }
- if (proc->buffers.next != &buffer->entry) {
- struct binder_buffer *prev = list_entry(buffer->entry.prev,
- struct binder_buffer, entry);
-
- if (prev->free) {
- binder_delete_free_buffer(proc, buffer);
- rb_erase(&prev->rb_node, &proc->free_buffers);
- buffer = prev;
- }
+ if (node_prio.prio < t->priority.prio ||
+ (node_prio.prio == t->priority.prio &&
+ node_prio.sched_policy == SCHED_FIFO)) {
+ /*
+ * In case the minimum priority on the node is
+ * higher (lower value), use that priority. If
+ * the priority is the same, but the node uses
+ * SCHED_FIFO, prefer SCHED_FIFO, since it can
+ * run unbounded, unlike SCHED_RR.
+ */
+ desired_prio = node_prio;
}
- binder_insert_free_buffer(proc, buffer);
+
+ binder_set_priority(task, desired_prio);
}
-static struct binder_node *binder_get_node(struct binder_proc *proc,
- binder_uintptr_t ptr)
+static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc,
+ binder_uintptr_t ptr)
{
struct rb_node *n = proc->nodes.rb_node;
struct binder_node *node;
+ assert_spin_locked(&proc->inner_lock);
+
while (n) {
node = rb_entry(n, struct binder_node, rb_node);
@@ -996,21 +1233,47 @@ static struct binder_node *binder_get_node(struct binder_proc *proc,
n = n->rb_left;
else if (ptr > node->ptr)
n = n->rb_right;
- else
+ else {
+ /*
+ * take an implicit weak reference
+ * to ensure node stays alive until
+ * call to binder_put_node()
+ */
+ binder_inc_node_tmpref_ilocked(node);
return node;
+ }
}
return NULL;
}
-static struct binder_node *binder_new_node(struct binder_proc *proc,
- binder_uintptr_t ptr,
- binder_uintptr_t cookie)
+static struct binder_node *binder_get_node(struct binder_proc *proc,
+ binder_uintptr_t ptr)
+{
+ struct binder_node *node;
+
+ binder_inner_proc_lock(proc);
+ node = binder_get_node_ilocked(proc, ptr);
+ binder_inner_proc_unlock(proc);
+ return node;
+}
+
+static struct binder_node *binder_init_node_ilocked(
+ struct binder_proc *proc,
+ struct binder_node *new_node,
+ struct flat_binder_object *fp)
{
struct rb_node **p = &proc->nodes.rb_node;
struct rb_node *parent = NULL;
struct binder_node *node;
+ binder_uintptr_t ptr = fp ? fp->binder : 0;
+ binder_uintptr_t cookie = fp ? fp->cookie : 0;
+ __u32 flags = fp ? fp->flags : 0;
+ s8 priority;
+
+ assert_spin_locked(&proc->inner_lock);
while (*p) {
+
parent = *p;
node = rb_entry(parent, struct binder_node, rb_node);
@@ -1018,14 +1281,19 @@ static struct binder_node *binder_new_node(struct binder_proc *proc,
p = &(*p)->rb_left;
else if (ptr > node->ptr)
p = &(*p)->rb_right;
- else
- return NULL;
+ else {
+ /*
+ * A matching node is already in
+ * the rb tree. Abandon the init
+ * and return it.
+ */
+ binder_inc_node_tmpref_ilocked(node);
+ return node;
+ }
}
-
- node = kzalloc_preempt_disabled(sizeof(*node));
- if (node == NULL)
- return NULL;
+ node = new_node;
binder_stats_created(BINDER_STAT_NODE);
+ node->tmp_refs++;
rb_link_node(&node->rb_node, parent, p);
rb_insert_color(&node->rb_node, &proc->nodes);
node->debug_id = atomic_inc_return(&binder_last_id);
@@ -1033,18 +1301,58 @@ static struct binder_node *binder_new_node(struct binder_proc *proc,
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE;
+ priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
+ node->sched_policy = (flags & FLAT_BINDER_FLAG_PRIORITY_MASK) >>
+ FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
+ node->min_priority = to_kernel_prio(node->sched_policy, priority);
+ node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
+ node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT);
+ spin_lock_init(&node->lock);
INIT_LIST_HEAD(&node->work.entry);
INIT_LIST_HEAD(&node->async_todo);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d:%d node %d u%016llx c%016llx created\n",
proc->pid, current->pid, node->debug_id,
(u64)node->ptr, (u64)node->cookie);
+
return node;
}
-static int binder_inc_node(struct binder_node *node, int strong, int internal,
- struct list_head *target_list)
+static struct binder_node *binder_new_node(struct binder_proc *proc,
+ struct flat_binder_object *fp)
+{
+ struct binder_node *node;
+ struct binder_node *new_node = kzalloc(sizeof(*node), GFP_KERNEL);
+
+ if (!new_node)
+ return NULL;
+ binder_inner_proc_lock(proc);
+ node = binder_init_node_ilocked(proc, new_node, fp);
+ binder_inner_proc_unlock(proc);
+ if (node != new_node)
+ /*
+ * The node was already added by another thread
+ */
+ kfree(new_node);
+
+ return node;
+}
+
+static void binder_free_node(struct binder_node *node)
+{
+ kfree(node);
+ binder_stats_deleted(BINDER_STAT_NODE);
+}
+
+static int binder_inc_node_nilocked(struct binder_node *node, int strong,
+ int internal,
+ struct list_head *target_list)
{
+ struct binder_proc *proc = node->proc;
+
+ assert_spin_locked(&node->lock);
+ if (proc)
+ assert_spin_locked(&proc->inner_lock);
if (strong) {
if (internal) {
if (target_list == NULL &&
@@ -1061,8 +1369,8 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal,
} else
node->local_strong_refs++;
if (!node->has_strong_ref && target_list) {
- list_del_init(&node->work.entry);
- list_add_tail(&node->work.entry, target_list);
+ binder_dequeue_work_ilocked(&node->work);
+ binder_enqueue_work_ilocked(&node->work, target_list);
}
} else {
if (!internal)
@@ -1073,58 +1381,169 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal,
node->debug_id);
return -EINVAL;
}
- list_add_tail(&node->work.entry, target_list);
+ binder_enqueue_work_ilocked(&node->work, target_list);
}
}
return 0;
}
-static int binder_dec_node(struct binder_node *node, int strong, int internal)
+static int binder_inc_node(struct binder_node *node, int strong, int internal,
+ struct list_head *target_list)
+{
+ int ret;
+
+ binder_node_inner_lock(node);
+ ret = binder_inc_node_nilocked(node, strong, internal, target_list);
+ binder_node_inner_unlock(node);
+
+ return ret;
+}
+
+static bool binder_dec_node_nilocked(struct binder_node *node,
+ int strong, int internal)
{
+ struct binder_proc *proc = node->proc;
+
+ assert_spin_locked(&node->lock);
+ if (proc)
+ assert_spin_locked(&proc->inner_lock);
if (strong) {
if (internal)
node->internal_strong_refs--;
else
node->local_strong_refs--;
if (node->local_strong_refs || node->internal_strong_refs)
- return 0;
+ return false;
} else {
if (!internal)
node->local_weak_refs--;
- if (node->local_weak_refs || !hlist_empty(&node->refs))
- return 0;
+ if (node->local_weak_refs || node->tmp_refs ||
+ !hlist_empty(&node->refs))
+ return false;
}
- if (node->proc && (node->has_strong_ref || node->has_weak_ref)) {
+
+ if (proc && (node->has_strong_ref || node->has_weak_ref)) {
if (list_empty(&node->work.entry)) {
- list_add_tail(&node->work.entry, &node->proc->todo);
- wake_up_interruptible(&node->proc->wait);
+ binder_enqueue_work_ilocked(&node->work, &proc->todo);
+ binder_wakeup_proc_ilocked(proc);
}
} else {
if (hlist_empty(&node->refs) && !node->local_strong_refs &&
- !node->local_weak_refs) {
- list_del_init(&node->work.entry);
- if (node->proc) {
- rb_erase(&node->rb_node, &node->proc->nodes);
+ !node->local_weak_refs && !node->tmp_refs) {
+ if (proc) {
+ binder_dequeue_work_ilocked(&node->work);
+ rb_erase(&node->rb_node, &proc->nodes);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"refless node %d deleted\n",
node->debug_id);
} else {
+ BUG_ON(!list_empty(&node->work.entry));
+ spin_lock(&binder_dead_nodes_lock);
+ /*
+ * tmp_refs could have changed so
+ * check it again
+ */
+ if (node->tmp_refs) {
+ spin_unlock(&binder_dead_nodes_lock);
+ return false;
+ }
hlist_del(&node->dead_node);
+ spin_unlock(&binder_dead_nodes_lock);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"dead node %d deleted\n",
node->debug_id);
}
- kfree(node);
- binder_stats_deleted(BINDER_STAT_NODE);
+ return true;
}
}
+ return false;
+}
- return 0;
+static void binder_dec_node(struct binder_node *node, int strong, int internal)
+{
+ bool free_node;
+
+ binder_node_inner_lock(node);
+ free_node = binder_dec_node_nilocked(node, strong, internal);
+ binder_node_inner_unlock(node);
+ if (free_node)
+ binder_free_node(node);
}
+static void binder_inc_node_tmpref_ilocked(struct binder_node *node)
+{
+ /*
+ * No call to binder_inc_node() is needed since we
+ * don't need to inform userspace of any changes to
+ * tmp_refs
+ */
+ node->tmp_refs++;
+}
+
+/**
+ * binder_inc_node_tmpref() - take a temporary reference on node
+ * @node: node to reference
+ *
+ * Take reference on node to prevent the node from being freed
+ * while referenced only by a local variable. The inner lock is
+ * needed to serialize with the node work on the queue (which
+ * isn't needed after the node is dead). If the node is dead
+ * (node->proc is NULL), use binder_dead_nodes_lock to protect
+ * node->tmp_refs against dead-node-only cases where the node
+ * lock cannot be acquired (eg traversing the dead node list to
+ * print nodes)
+ */
+static void binder_inc_node_tmpref(struct binder_node *node)
+{
+ binder_node_lock(node);
+ if (node->proc)
+ binder_inner_proc_lock(node->proc);
+ else
+ spin_lock(&binder_dead_nodes_lock);
+ binder_inc_node_tmpref_ilocked(node);
+ if (node->proc)
+ binder_inner_proc_unlock(node->proc);
+ else
+ spin_unlock(&binder_dead_nodes_lock);
+ binder_node_unlock(node);
+}
+
+/**
+ * binder_dec_node_tmpref() - remove a temporary reference on node
+ * @node: node to reference
+ *
+ * Release temporary reference on node taken via binder_inc_node_tmpref()
+ */
+static void binder_dec_node_tmpref(struct binder_node *node)
+{
+ bool free_node;
+
+ binder_node_inner_lock(node);
+ if (!node->proc)
+ spin_lock(&binder_dead_nodes_lock);
+ node->tmp_refs--;
+ BUG_ON(node->tmp_refs < 0);
+ if (!node->proc)
+ spin_unlock(&binder_dead_nodes_lock);
+ /*
+ * Call binder_dec_node() to check if all refcounts are 0
+ * and cleanup is needed. Calling with strong=0 and internal=1
+ * causes no actual reference to be released in binder_dec_node().
+ * If that changes, a change is needed here too.
+ */
+ free_node = binder_dec_node_nilocked(node, 0, 1);
+ binder_node_inner_unlock(node);
+ if (free_node)
+ binder_free_node(node);
+}
+
+static void binder_put_node(struct binder_node *node)
+{
+ binder_dec_node_tmpref(node);
+}
-static struct binder_ref *binder_get_ref(struct binder_proc *proc,
- u32 desc, bool need_strong_ref)
+static struct binder_ref *binder_get_ref_olocked(struct binder_proc *proc,
+ u32 desc, bool need_strong_ref)
{
struct rb_node *n = proc->refs_by_desc.rb_node;
struct binder_ref *ref;
@@ -1132,11 +1551,11 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc,
while (n) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
- if (desc < ref->desc) {
+ if (desc < ref->data.desc) {
n = n->rb_left;
- } else if (desc > ref->desc) {
+ } else if (desc > ref->data.desc) {
n = n->rb_right;
- } else if (need_strong_ref && !ref->strong) {
+ } else if (need_strong_ref && !ref->data.strong) {
binder_user_error("tried to use weak ref as strong ref\n");
return NULL;
} else {
@@ -1146,14 +1565,34 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc,
return NULL;
}
-static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
- struct binder_node *node)
+/**
+ * binder_get_ref_for_node_olocked() - get the ref associated with given node
+ * @proc: binder_proc that owns the ref
+ * @node: binder_node of target
+ * @new_ref: newly allocated binder_ref to be initialized or %NULL
+ *
+ * Look up the ref for the given node and return it if it exists
+ *
+ * If it doesn't exist and the caller provides a newly allocated
+ * ref, initialize the fields of the newly allocated ref and insert
+ * into the given proc rb_trees and node refs list.
+ *
+ * Return: the ref for node. It is possible that another thread
+ * allocated/initialized the ref first in which case the
+ * returned ref would be different than the passed-in
+ * new_ref. new_ref must be kfree'd by the caller in
+ * this case.
+ */
+static struct binder_ref *binder_get_ref_for_node_olocked(
+ struct binder_proc *proc,
+ struct binder_node *node,
+ struct binder_ref *new_ref)
{
- struct rb_node *n;
+ struct binder_context *context = proc->context;
struct rb_node **p = &proc->refs_by_node.rb_node;
struct rb_node *parent = NULL;
- struct binder_ref *ref, *new_ref;
- struct binder_context *context = proc->context;
+ struct binder_ref *ref;
+ struct rb_node *n;
while (*p) {
parent = *p;
@@ -1166,22 +1605,22 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
else
return ref;
}
- new_ref = kzalloc_preempt_disabled(sizeof(*ref));
- if (new_ref == NULL)
+ if (!new_ref)
return NULL;
+
binder_stats_created(BINDER_STAT_REF);
- new_ref->debug_id = atomic_inc_return(&binder_last_id);
+ new_ref->data.debug_id = atomic_inc_return(&binder_last_id);
new_ref->proc = proc;
new_ref->node = node;
rb_link_node(&new_ref->rb_node_node, parent, p);
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
- new_ref->desc = (node == context->binder_context_mgr_node) ? 0 : 1;
+ new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1;
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
- if (ref->desc > new_ref->desc)
+ if (ref->data.desc > new_ref->data.desc)
break;
- new_ref->desc = ref->desc + 1;
+ new_ref->data.desc = ref->data.desc + 1;
}
p = &proc->refs_by_desc.rb_node;
@@ -1189,121 +1628,423 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
parent = *p;
ref = rb_entry(parent, struct binder_ref, rb_node_desc);
- if (new_ref->desc < ref->desc)
+ if (new_ref->data.desc < ref->data.desc)
p = &(*p)->rb_left;
- else if (new_ref->desc > ref->desc)
+ else if (new_ref->data.desc > ref->data.desc)
p = &(*p)->rb_right;
else
BUG();
}
rb_link_node(&new_ref->rb_node_desc, parent, p);
rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);
- if (node) {
- hlist_add_head(&new_ref->node_entry, &node->refs);
- binder_debug(BINDER_DEBUG_INTERNAL_REFS,
- "%d new ref %d desc %d for node %d\n",
- proc->pid, new_ref->debug_id, new_ref->desc,
- node->debug_id);
- } else {
- binder_debug(BINDER_DEBUG_INTERNAL_REFS,
- "%d new ref %d desc %d for dead node\n",
- proc->pid, new_ref->debug_id, new_ref->desc);
- }
+ binder_node_lock(node);
+ hlist_add_head(&new_ref->node_entry, &node->refs);
+
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "%d new ref %d desc %d for node %d\n",
+ proc->pid, new_ref->data.debug_id, new_ref->data.desc,
+ node->debug_id);
+ binder_node_unlock(node);
return new_ref;
}
-static void binder_delete_ref(struct binder_ref *ref)
+static void binder_cleanup_ref_olocked(struct binder_ref *ref)
{
+ bool delete_node = false;
+
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d delete ref %d desc %d for node %d\n",
- ref->proc->pid, ref->debug_id, ref->desc,
+ ref->proc->pid, ref->data.debug_id, ref->data.desc,
ref->node->debug_id);
rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
- if (ref->strong)
- binder_dec_node(ref->node, 1, 1);
+
+ binder_node_inner_lock(ref->node);
+ if (ref->data.strong)
+ binder_dec_node_nilocked(ref->node, 1, 1);
+
hlist_del(&ref->node_entry);
- binder_dec_node(ref->node, 0, 1);
+ delete_node = binder_dec_node_nilocked(ref->node, 0, 1);
+ binder_node_inner_unlock(ref->node);
+ /*
+ * Clear ref->node unless we want the caller to free the node
+ */
+ if (!delete_node) {
+ /*
+ * The caller uses ref->node to determine
+ * whether the node needs to be freed. Clear
+ * it since the node is still alive.
+ */
+ ref->node = NULL;
+ }
+
if (ref->death) {
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"%d delete ref %d desc %d has death notification\n",
- ref->proc->pid, ref->debug_id, ref->desc);
- list_del(&ref->death->work.entry);
- kfree(ref->death);
+ ref->proc->pid, ref->data.debug_id,
+ ref->data.desc);
+ binder_dequeue_work(ref->proc, &ref->death->work);
binder_stats_deleted(BINDER_STAT_DEATH);
}
- kfree(ref);
binder_stats_deleted(BINDER_STAT_REF);
}
-static int binder_inc_ref(struct binder_ref *ref, int strong,
- struct list_head *target_list)
+/**
+ * binder_inc_ref_olocked() - increment the ref for given handle
+ * @ref: ref to be incremented
+ * @strong: if true, strong increment, else weak
+ * @target_list: list to queue node work on
+ *
+ * Increment the ref. @ref->proc->outer_lock must be held on entry
+ *
+ * Return: 0, if successful, else errno
+ */
+static int binder_inc_ref_olocked(struct binder_ref *ref, int strong,
+ struct list_head *target_list)
{
int ret;
if (strong) {
- if (ref->strong == 0) {
+ if (ref->data.strong == 0) {
ret = binder_inc_node(ref->node, 1, 1, target_list);
if (ret)
return ret;
}
- ref->strong++;
+ ref->data.strong++;
} else {
- if (ref->weak == 0) {
+ if (ref->data.weak == 0) {
ret = binder_inc_node(ref->node, 0, 1, target_list);
if (ret)
return ret;
}
- ref->weak++;
+ ref->data.weak++;
}
return 0;
}
-
-static int binder_dec_ref(struct binder_ref *ref, int strong)
+/**
+ * binder_dec_ref() - dec the ref for given handle
+ * @ref: ref to be decremented
+ * @strong: if true, strong decrement, else weak
+ *
+ * Decrement the ref.
+ *
+ * Return: true if ref is cleaned up and ready to be freed
+ */
+static bool binder_dec_ref_olocked(struct binder_ref *ref, int strong)
{
if (strong) {
- if (ref->strong == 0) {
+ if (ref->data.strong == 0) {
binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n",
- ref->proc->pid, ref->debug_id,
- ref->desc, ref->strong, ref->weak);
- return -EINVAL;
- }
- ref->strong--;
- if (ref->strong == 0) {
- int ret;
-
- ret = binder_dec_node(ref->node, strong, 1);
- if (ret)
- return ret;
+ ref->proc->pid, ref->data.debug_id,
+ ref->data.desc, ref->data.strong,
+ ref->data.weak);
+ return false;
}
+ ref->data.strong--;
+ if (ref->data.strong == 0)
+ binder_dec_node(ref->node, strong, 1);
} else {
- if (ref->weak == 0) {
+ if (ref->data.weak == 0) {
binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n",
- ref->proc->pid, ref->debug_id,
- ref->desc, ref->strong, ref->weak);
- return -EINVAL;
+ ref->proc->pid, ref->data.debug_id,
+ ref->data.desc, ref->data.strong,
+ ref->data.weak);
+ return false;
}
- ref->weak--;
+ ref->data.weak--;
}
- if (ref->strong == 0 && ref->weak == 0)
- binder_delete_ref(ref);
- return 0;
+ if (ref->data.strong == 0 && ref->data.weak == 0) {
+ binder_cleanup_ref_olocked(ref);
+ return true;
+ }
+ return false;
}
-static void binder_pop_transaction(struct binder_thread *target_thread,
- struct binder_transaction *t)
+/**
+ * binder_get_node_from_ref() - get the node from the given proc/desc
+ * @proc: proc containing the ref
+ * @desc: the handle associated with the ref
+ * @need_strong_ref: if true, only return node if ref is strong
+ * @rdata: the id/refcount data for the ref
+ *
+ * Given a proc and ref handle, return the associated binder_node
+ *
+ * Return: a binder_node or NULL if not found or not strong when strong required
+ */
+static struct binder_node *binder_get_node_from_ref(
+ struct binder_proc *proc,
+ u32 desc, bool need_strong_ref,
+ struct binder_ref_data *rdata)
{
- if (target_thread) {
- BUG_ON(target_thread->transaction_stack != t);
- BUG_ON(target_thread->transaction_stack->from != target_thread);
- target_thread->transaction_stack =
- target_thread->transaction_stack->from_parent;
- t->from = NULL;
+ struct binder_node *node;
+ struct binder_ref *ref;
+
+ binder_proc_lock(proc);
+ ref = binder_get_ref_olocked(proc, desc, need_strong_ref);
+ if (!ref)
+ goto err_no_ref;
+ node = ref->node;
+ /*
+ * Take an implicit reference on the node to ensure
+ * it stays alive until the call to binder_put_node()
+ */
+ binder_inc_node_tmpref(node);
+ if (rdata)
+ *rdata = ref->data;
+ binder_proc_unlock(proc);
+
+ return node;
+
+err_no_ref:
+ binder_proc_unlock(proc);
+ return NULL;
+}
+
+/**
+ * binder_free_ref() - free the binder_ref
+ * @ref: ref to free
+ *
+ * Free the binder_ref. Free the binder_node indicated by ref->node
+ * (if non-NULL) and the binder_ref_death indicated by ref->death.
+ */
+static void binder_free_ref(struct binder_ref *ref)
+{
+ if (ref->node)
+ binder_free_node(ref->node);
+ kfree(ref->death);
+ kfree(ref);
+}
+
+/**
+ * binder_update_ref_for_handle() - inc/dec the ref for given handle
+ * @proc: proc containing the ref
+ * @desc: the handle associated with the ref
+ * @increment: true=inc reference, false=dec reference
+ * @strong: true=strong reference, false=weak reference
+ * @rdata: the id/refcount data for the ref
+ *
+ * Given a proc and ref handle, increment or decrement the ref
+ * according to "increment" arg.
+ *
+ * Return: 0 if successful, else errno
+ */
+static int binder_update_ref_for_handle(struct binder_proc *proc,
+ uint32_t desc, bool increment, bool strong,
+ struct binder_ref_data *rdata)
+{
+ int ret = 0;
+ struct binder_ref *ref;
+ bool delete_ref = false;
+
+ binder_proc_lock(proc);
+ ref = binder_get_ref_olocked(proc, desc, strong);
+ if (!ref) {
+ ret = -EINVAL;
+ goto err_no_ref;
+ }
+ if (increment)
+ ret = binder_inc_ref_olocked(ref, strong, NULL);
+ else
+ delete_ref = binder_dec_ref_olocked(ref, strong);
+
+ if (rdata)
+ *rdata = ref->data;
+ binder_proc_unlock(proc);
+
+ if (delete_ref)
+ binder_free_ref(ref);
+ return ret;
+
+err_no_ref:
+ binder_proc_unlock(proc);
+ return ret;
+}
+
+/**
+ * binder_dec_ref_for_handle() - dec the ref for given handle
+ * @proc: proc containing the ref
+ * @desc: the handle associated with the ref
+ * @strong: true=strong reference, false=weak reference
+ * @rdata: the id/refcount data for the ref
+ *
+ * Just calls binder_update_ref_for_handle() to decrement the ref.
+ *
+ * Return: 0 if successful, else errno
+ */
+static int binder_dec_ref_for_handle(struct binder_proc *proc,
+ uint32_t desc, bool strong, struct binder_ref_data *rdata)
+{
+ return binder_update_ref_for_handle(proc, desc, false, strong, rdata);
+}
+
+
+/**
+ * binder_inc_ref_for_node() - increment the ref for given proc/node
+ * @proc: proc containing the ref
+ * @node: target node
+ * @strong: true=strong reference, false=weak reference
+ * @target_list: worklist to use if node is incremented
+ * @rdata: the id/refcount data for the ref
+ *
+ * Given a proc and node, increment the ref. Create the ref if it
+ * doesn't already exist
+ *
+ * Return: 0 if successful, else errno
+ */
+static int binder_inc_ref_for_node(struct binder_proc *proc,
+ struct binder_node *node,
+ bool strong,
+ struct list_head *target_list,
+ struct binder_ref_data *rdata)
+{
+ struct binder_ref *ref;
+ struct binder_ref *new_ref = NULL;
+ int ret = 0;
+
+ binder_proc_lock(proc);
+ ref = binder_get_ref_for_node_olocked(proc, node, NULL);
+ if (!ref) {
+ binder_proc_unlock(proc);
+ new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!new_ref)
+ return -ENOMEM;
+ binder_proc_lock(proc);
+ ref = binder_get_ref_for_node_olocked(proc, node, new_ref);
+ }
+ ret = binder_inc_ref_olocked(ref, strong, target_list);
+ *rdata = ref->data;
+ binder_proc_unlock(proc);
+ if (new_ref && ref != new_ref)
+ /*
+ * Another thread created the ref first so
+ * free the one we allocated
+ */
+ kfree(new_ref);
+ return ret;
+}
+
+static void binder_pop_transaction_ilocked(struct binder_thread *target_thread,
+ struct binder_transaction *t)
+{
+ BUG_ON(!target_thread);
+ assert_spin_locked(&target_thread->proc->inner_lock);
+ BUG_ON(target_thread->transaction_stack != t);
+ BUG_ON(target_thread->transaction_stack->from != target_thread);
+ target_thread->transaction_stack =
+ target_thread->transaction_stack->from_parent;
+ t->from = NULL;
+}
+
+/**
+ * binder_thread_dec_tmpref() - decrement thread->tmp_ref
+ * @thread: thread to decrement
+ *
+ * A thread needs to be kept alive while being used to create or
+ * handle a transaction. binder_get_txn_from() is used to safely
+ * extract t->from from a binder_transaction and keep the thread
+ * indicated by t->from from being freed. When done with that
+ * binder_thread, this function is called to decrement the
+ * tmp_ref and free if appropriate (thread has been released
+ * and no transaction being processed by the driver)
+ */
+static void binder_thread_dec_tmpref(struct binder_thread *thread)
+{
+ /*
+ * atomic is used to protect the counter value while
+ * it cannot reach zero or thread->is_dead is false
+ */
+ binder_inner_proc_lock(thread->proc);
+ atomic_dec(&thread->tmp_ref);
+ if (thread->is_dead && !atomic_read(&thread->tmp_ref)) {
+ binder_inner_proc_unlock(thread->proc);
+ binder_free_thread(thread);
+ return;
}
- t->need_reply = 0;
+ binder_inner_proc_unlock(thread->proc);
+}
+
+/**
+ * binder_proc_dec_tmpref() - decrement proc->tmp_ref
+ * @proc: proc to decrement
+ *
+ * A binder_proc needs to be kept alive while being used to create or
+ * handle a transaction. proc->tmp_ref is incremented when
+ * creating a new transaction or the binder_proc is currently in-use
+ * by threads that are being released. When done with the binder_proc,
+ * this function is called to decrement the counter and free the
+ * proc if appropriate (proc has been released, all threads have
+ * been released and not currenly in-use to process a transaction).
+ */
+static void binder_proc_dec_tmpref(struct binder_proc *proc)
+{
+ binder_inner_proc_lock(proc);
+ proc->tmp_ref--;
+ if (proc->is_dead && RB_EMPTY_ROOT(&proc->threads) &&
+ !proc->tmp_ref) {
+ binder_inner_proc_unlock(proc);
+ binder_free_proc(proc);
+ return;
+ }
+ binder_inner_proc_unlock(proc);
+}
+
+/**
+ * binder_get_txn_from() - safely extract the "from" thread in transaction
+ * @t: binder transaction for t->from
+ *
+ * Atomically return the "from" thread and increment the tmp_ref
+ * count for the thread to ensure it stays alive until
+ * binder_thread_dec_tmpref() is called.
+ *
+ * Return: the value of t->from
+ */
+static struct binder_thread *binder_get_txn_from(
+ struct binder_transaction *t)
+{
+ struct binder_thread *from;
+
+ spin_lock(&t->lock);
+ from = t->from;
+ if (from)
+ atomic_inc(&from->tmp_ref);
+ spin_unlock(&t->lock);
+ return from;
+}
+
+/**
+ * binder_get_txn_from_and_acq_inner() - get t->from and acquire inner lock
+ * @t: binder transaction for t->from
+ *
+ * Same as binder_get_txn_from() except it also acquires the proc->inner_lock
+ * to guarantee that the thread cannot be released while operating on it.
+ * The caller must call binder_inner_proc_unlock() to release the inner lock
+ * as well as call binder_dec_thread_txn() to release the reference.
+ *
+ * Return: the value of t->from
+ */
+static struct binder_thread *binder_get_txn_from_and_acq_inner(
+ struct binder_transaction *t)
+{
+ struct binder_thread *from;
+
+ from = binder_get_txn_from(t);
+ if (!from)
+ return NULL;
+ binder_inner_proc_lock(from->proc);
+ if (t->from) {
+ BUG_ON(from != t->from);
+ return from;
+ }
+ binder_inner_proc_unlock(from->proc);
+ binder_thread_dec_tmpref(from);
+ return NULL;
+}
+
+static void binder_free_transaction(struct binder_transaction *t)
+{
if (t->buffer)
t->buffer->transaction = NULL;
kfree(t);
@@ -1318,30 +2059,28 @@ static void binder_send_failed_reply(struct binder_transaction *t,
BUG_ON(t->flags & TF_ONE_WAY);
while (1) {
- target_thread = t->from;
+ target_thread = binder_get_txn_from_and_acq_inner(t);
if (target_thread) {
- if (target_thread->return_error != BR_OK &&
- target_thread->return_error2 == BR_OK) {
- target_thread->return_error2 =
- target_thread->return_error;
- target_thread->return_error = BR_OK;
- }
- if (target_thread->return_error == BR_OK) {
- binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
- "send failed reply for transaction %d to %d:%d\n",
- t->debug_id,
- target_thread->proc->pid,
- target_thread->pid);
-
- binder_pop_transaction(target_thread, t);
- target_thread->return_error = error_code;
+ binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
+ "send failed reply for transaction %d to %d:%d\n",
+ t->debug_id,
+ target_thread->proc->pid,
+ target_thread->pid);
+
+ binder_pop_transaction_ilocked(target_thread, t);
+ if (target_thread->reply_error.cmd == BR_OK) {
+ target_thread->reply_error.cmd = error_code;
+ binder_enqueue_work_ilocked(
+ &target_thread->reply_error.work,
+ &target_thread->todo);
wake_up_interruptible(&target_thread->wait);
} else {
- pr_err("reply failed, target thread, %d:%d, has error code %d already\n",
- target_thread->proc->pid,
- target_thread->pid,
- target_thread->return_error);
+ WARN(1, "Unexpected reply error: %u\n",
+ target_thread->reply_error.cmd);
}
+ binder_inner_proc_unlock(target_thread->proc);
+ binder_thread_dec_tmpref(target_thread);
+ binder_free_transaction(t);
return;
}
next = t->from_parent;
@@ -1350,7 +2089,7 @@ static void binder_send_failed_reply(struct binder_transaction *t,
"send failed reply for transaction %d, target dead\n",
t->debug_id);
- binder_pop_transaction(target_thread, t);
+ binder_free_transaction(t);
if (next == NULL) {
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"reply failed, no target thread at root\n");
@@ -1559,25 +2298,26 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
node->debug_id, (u64)node->ptr);
binder_dec_node(node, hdr->type == BINDER_TYPE_BINDER,
0);
+ binder_put_node(node);
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
struct flat_binder_object *fp;
- struct binder_ref *ref;
+ struct binder_ref_data rdata;
+ int ret;
fp = to_flat_binder_object(hdr);
- ref = binder_get_ref(proc, fp->handle,
- hdr->type == BINDER_TYPE_HANDLE);
+ ret = binder_dec_ref_for_handle(proc, fp->handle,
+ hdr->type == BINDER_TYPE_HANDLE, &rdata);
- if (ref == NULL) {
- pr_err("transaction release %d bad handle %d\n",
- debug_id, fp->handle);
+ if (ret) {
+ pr_err("transaction release %d bad handle %d, ret = %d\n",
+ debug_id, fp->handle, ret);
break;
}
binder_debug(BINDER_DEBUG_TRANSACTION,
- " ref %d desc %d (node %d)\n",
- ref->debug_id, ref->desc, ref->node->debug_id);
- binder_dec_ref(ref, hdr->type == BINDER_TYPE_HANDLE);
+ " ref %d desc %d\n",
+ rdata.debug_id, rdata.desc);
} break;
case BINDER_TYPE_FD: {
@@ -1616,7 +2356,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
* back to kernel address space to access it
*/
parent_buffer = parent->buffer -
- proc->user_buffer_offset;
+ binder_alloc_get_user_buffer_offset(
+ &proc->alloc);
fd_buf_size = sizeof(u32) * fda->num_fds;
if (fda->num_fds >= SIZE_MAX / sizeof(u32)) {
@@ -1648,102 +2389,122 @@ static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_thread *thread)
{
struct binder_node *node;
- struct binder_ref *ref;
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;
+ struct binder_ref_data rdata;
+ int ret = 0;
node = binder_get_node(proc, fp->binder);
if (!node) {
- node = binder_new_node(proc, fp->binder, fp->cookie);
+ node = binder_new_node(proc, fp);
if (!node)
return -ENOMEM;
-
- node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
- node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
}
if (fp->cookie != node->cookie) {
binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
proc->pid, thread->pid, (u64)fp->binder,
node->debug_id, (u64)fp->cookie,
(u64)node->cookie);
- return -EINVAL;
+ ret = -EINVAL;
+ goto done;
+ }
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
+ ret = -EPERM;
+ goto done;
}
- if (security_binder_transfer_binder(proc->tsk, target_proc->tsk))
- return -EPERM;
- ref = binder_get_ref_for_node(target_proc, node);
- if (!ref)
- return -EINVAL;
+ ret = binder_inc_ref_for_node(target_proc, node,
+ fp->hdr.type == BINDER_TYPE_BINDER,
+ &thread->todo, &rdata);
+ if (ret)
+ goto done;
if (fp->hdr.type == BINDER_TYPE_BINDER)
fp->hdr.type = BINDER_TYPE_HANDLE;
else
fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
- fp->handle = ref->desc;
+ fp->handle = rdata.desc;
fp->cookie = 0;
- binder_inc_ref(ref, fp->hdr.type == BINDER_TYPE_HANDLE, &thread->todo);
- trace_binder_transaction_node_to_ref(t, node, ref);
+ trace_binder_transaction_node_to_ref(t, node, &rdata);
binder_debug(BINDER_DEBUG_TRANSACTION,
" node %d u%016llx -> ref %d desc %d\n",
node->debug_id, (u64)node->ptr,
- ref->debug_id, ref->desc);
-
- return 0;
+ rdata.debug_id, rdata.desc);
+done:
+ binder_put_node(node);
+ return ret;
}
static int binder_translate_handle(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread)
{
- struct binder_ref *ref;
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;
+ struct binder_node *node;
+ struct binder_ref_data src_rdata;
+ int ret = 0;
- ref = binder_get_ref(proc, fp->handle,
- fp->hdr.type == BINDER_TYPE_HANDLE);
- if (!ref) {
+ node = binder_get_node_from_ref(proc, fp->handle,
+ fp->hdr.type == BINDER_TYPE_HANDLE, &src_rdata);
+ if (!node) {
binder_user_error("%d:%d got transaction with invalid handle, %d\n",
proc->pid, thread->pid, fp->handle);
return -EINVAL;
}
- if (security_binder_transfer_binder(proc->tsk, target_proc->tsk))
- return -EPERM;
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
+ ret = -EPERM;
+ goto done;
+ }
- if (ref->node->proc == target_proc) {
+ binder_node_lock(node);
+ if (node->proc == target_proc) {
if (fp->hdr.type == BINDER_TYPE_HANDLE)
fp->hdr.type = BINDER_TYPE_BINDER;
else
fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
- fp->binder = ref->node->ptr;
- fp->cookie = ref->node->cookie;
- binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER,
- 0, NULL);
- trace_binder_transaction_ref_to_node(t, ref);
+ fp->binder = node->ptr;
+ fp->cookie = node->cookie;
+ if (node->proc)
+ binder_inner_proc_lock(node->proc);
+ binder_inc_node_nilocked(node,
+ fp->hdr.type == BINDER_TYPE_BINDER,
+ 0, NULL);
+ if (node->proc)
+ binder_inner_proc_unlock(node->proc);
+ trace_binder_transaction_ref_to_node(t, node, &src_rdata);
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> node %d u%016llx\n",
- ref->debug_id, ref->desc, ref->node->debug_id,
- (u64)ref->node->ptr);
+ src_rdata.debug_id, src_rdata.desc, node->debug_id,
+ (u64)node->ptr);
+ binder_node_unlock(node);
} else {
- struct binder_ref *new_ref;
+ int ret;
+ struct binder_ref_data dest_rdata;
- new_ref = binder_get_ref_for_node(target_proc, ref->node);
- if (!new_ref)
- return -EINVAL;
+ binder_node_unlock(node);
+ ret = binder_inc_ref_for_node(target_proc, node,
+ fp->hdr.type == BINDER_TYPE_HANDLE,
+ NULL, &dest_rdata);
+ if (ret)
+ goto done;
fp->binder = 0;
- fp->handle = new_ref->desc;
+ fp->handle = dest_rdata.desc;
fp->cookie = 0;
- binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE,
- NULL);
- trace_binder_transaction_ref_to_ref(t, ref, new_ref);
+ trace_binder_transaction_ref_to_ref(t, node, &src_rdata,
+ &dest_rdata);
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> ref %d desc %d (node %d)\n",
- ref->debug_id, ref->desc, new_ref->debug_id,
- new_ref->desc, ref->node->debug_id);
+ src_rdata.debug_id, src_rdata.desc,
+ dest_rdata.debug_id, dest_rdata.desc,
+ node->debug_id);
}
- return 0;
+done:
+ binder_put_node(node);
+ return ret;
}
static int binder_translate_fd(int fd,
@@ -1834,7 +2595,8 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda,
* Since the parent was already fixed up, convert it
* back to the kernel address space to access it
*/
- parent_buffer = parent->buffer - target_proc->user_buffer_offset;
+ parent_buffer = parent->buffer -
+ binder_alloc_get_user_buffer_offset(&target_proc->alloc);
fd_array = (u32 *)(parent_buffer + fda->parent_offset);
if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) {
binder_user_error("%d:%d parent offset not aligned correctly.\n",
@@ -1902,12 +2664,87 @@ static int binder_fixup_parent(struct binder_transaction *t,
return -EINVAL;
}
parent_buffer = (u8 *)(parent->buffer -
- target_proc->user_buffer_offset);
+ binder_alloc_get_user_buffer_offset(
+ &target_proc->alloc));
*(binder_uintptr_t *)(parent_buffer + bp->parent_offset) = bp->buffer;
return 0;
}
+/**
+ * binder_proc_transaction() - sends a transaction to a process and wakes it up
+ * @t: transaction to send
+ * @proc: process to send the transaction to
+ * @thread: thread in @proc to send the transaction to (may be NULL)
+ *
+ * This function queues a transaction to the specified process. It will try
+ * to find a thread in the target process to handle the transaction and
+ * wake it up. If no thread is found, the work is queued to the proc
+ * waitqueue.
+ *
+ * If the @thread parameter is not NULL, the transaction is always queued
+ * to the waitlist of that specific thread.
+ *
+ * Return: true if the transactions was successfully queued
+ * false if the target process or thread is dead
+ */
+static bool binder_proc_transaction(struct binder_transaction *t,
+ struct binder_proc *proc,
+ struct binder_thread *thread)
+{
+ struct list_head *target_list = NULL;
+ struct binder_node *node = t->buffer->target_node;
+ struct binder_priority node_prio;
+ bool oneway = !!(t->flags & TF_ONE_WAY);
+ bool wakeup = true;
+
+ BUG_ON(!node);
+ binder_node_lock(node);
+ node_prio.prio = node->min_priority;
+ node_prio.sched_policy = node->sched_policy;
+
+ if (oneway) {
+ BUG_ON(thread);
+ if (node->has_async_transaction) {
+ target_list = &node->async_todo;
+ wakeup = false;
+ } else {
+ node->has_async_transaction = 1;
+ }
+ }
+
+ binder_inner_proc_lock(proc);
+
+ if (proc->is_dead || (thread && thread->is_dead)) {
+ binder_inner_proc_unlock(proc);
+ binder_node_unlock(node);
+ return false;
+ }
+
+ if (!thread && !target_list)
+ thread = binder_select_thread_ilocked(proc);
+
+ if (thread) {
+ target_list = &thread->todo;
+ binder_transaction_priority(thread->task, t, node_prio,
+ node->inherit_rt);
+ } else if (!target_list) {
+ target_list = &proc->todo;
+ } else {
+ BUG_ON(target_list != &node->async_todo);
+ }
+
+ binder_enqueue_work_ilocked(&t->work, target_list);
+
+ if (wakeup)
+ binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);
+
+ binder_inner_proc_unlock(proc);
+ binder_node_unlock(node);
+
+ return true;
+}
+
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
@@ -1919,19 +2756,21 @@ static void binder_transaction(struct binder_proc *proc,
binder_size_t *offp, *off_end, *off_start;
binder_size_t off_min;
u8 *sg_bufp, *sg_buf_end;
- struct binder_proc *target_proc;
+ struct binder_proc *target_proc = NULL;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
- struct list_head *target_list;
- wait_queue_head_t *target_wait;
struct binder_transaction *in_reply_to = NULL;
struct binder_transaction_log_entry *e;
- uint32_t return_error;
+ uint32_t return_error = 0;
+ uint32_t return_error_param = 0;
+ uint32_t return_error_line = 0;
struct binder_buffer_object *last_fixup_obj = NULL;
binder_size_t last_fixup_min_off = 0;
struct binder_context *context = proc->context;
+ int t_debug_id = atomic_inc_return(&binder_last_id);
- e = binder_transaction_log_add(&context->transaction_log);
+ e = binder_transaction_log_add(&binder_transaction_log);
+ e->debug_id = t_debug_id;
e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
e->from_proc = proc->pid;
e->from_thread = thread->pid;
@@ -1941,29 +2780,39 @@ static void binder_transaction(struct binder_proc *proc,
e->context_name = proc->context->name;
if (reply) {
+ binder_inner_proc_lock(proc);
in_reply_to = thread->transaction_stack;
if (in_reply_to == NULL) {
+ binder_inner_proc_unlock(proc);
binder_user_error("%d:%d got reply transaction with no transaction stack\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EPROTO;
+ return_error_line = __LINE__;
goto err_empty_call_stack;
}
- binder_set_nice(in_reply_to->saved_priority);
if (in_reply_to->to_thread != thread) {
+ spin_lock(&in_reply_to->lock);
binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n",
proc->pid, thread->pid, in_reply_to->debug_id,
in_reply_to->to_proc ?
in_reply_to->to_proc->pid : 0,
in_reply_to->to_thread ?
in_reply_to->to_thread->pid : 0);
+ spin_unlock(&in_reply_to->lock);
+ binder_inner_proc_unlock(proc);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EPROTO;
+ return_error_line = __LINE__;
in_reply_to = NULL;
goto err_bad_call_stack;
}
thread->transaction_stack = in_reply_to->to_parent;
- target_thread = in_reply_to->from;
+ binder_inner_proc_unlock(proc);
+ target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);
if (target_thread == NULL) {
return_error = BR_DEAD_REPLY;
+ return_error_line = __LINE__;
goto err_dead_binder;
}
if (target_thread->transaction_stack != in_reply_to) {
@@ -1972,89 +2821,137 @@ static void binder_transaction(struct binder_proc *proc,
target_thread->transaction_stack ?
target_thread->transaction_stack->debug_id : 0,
in_reply_to->debug_id);
+ binder_inner_proc_unlock(target_thread->proc);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EPROTO;
+ return_error_line = __LINE__;
in_reply_to = NULL;
target_thread = NULL;
goto err_dead_binder;
}
target_proc = target_thread->proc;
+ target_proc->tmp_ref++;
+ binder_inner_proc_unlock(target_thread->proc);
} else {
if (tr->target.handle) {
struct binder_ref *ref;
- ref = binder_get_ref(proc, tr->target.handle, true);
- if (ref == NULL) {
+ /*
+ * There must already be a strong ref
+ * on this node. If so, do a strong
+ * increment on the node to ensure it
+ * stays alive until the transaction is
+ * done.
+ */
+ binder_proc_lock(proc);
+ ref = binder_get_ref_olocked(proc, tr->target.handle,
+ true);
+ if (ref) {
+ binder_inc_node(ref->node, 1, 0, NULL);
+ target_node = ref->node;
+ }
+ binder_proc_unlock(proc);
+ if (target_node == NULL) {
binder_user_error("%d:%d got transaction to invalid handle\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
goto err_invalid_target_handle;
}
- target_node = ref->node;
} else {
+ mutex_lock(&context->context_mgr_node_lock);
target_node = context->binder_context_mgr_node;
if (target_node == NULL) {
return_error = BR_DEAD_REPLY;
+ mutex_unlock(&context->context_mgr_node_lock);
+ return_error_line = __LINE__;
goto err_no_context_mgr_node;
}
+ binder_inc_node(target_node, 1, 0, NULL);
+ mutex_unlock(&context->context_mgr_node_lock);
}
e->to_node = target_node->debug_id;
+ binder_node_lock(target_node);
target_proc = target_node->proc;
if (target_proc == NULL) {
+ binder_node_unlock(target_node);
return_error = BR_DEAD_REPLY;
+ return_error_line = __LINE__;
goto err_dead_binder;
}
+ binder_inner_proc_lock(target_proc);
+ target_proc->tmp_ref++;
+ binder_inner_proc_unlock(target_proc);
+ binder_node_unlock(target_node);
if (security_binder_transaction(proc->tsk,
target_proc->tsk) < 0) {
return_error = BR_FAILED_REPLY;
+ return_error_param = -EPERM;
+ return_error_line = __LINE__;
goto err_invalid_target_handle;
}
+ binder_inner_proc_lock(proc);
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
tmp = thread->transaction_stack;
if (tmp->to_thread != thread) {
+ spin_lock(&tmp->lock);
binder_user_error("%d:%d got new transaction with bad transaction stack, transaction %d has target %d:%d\n",
proc->pid, thread->pid, tmp->debug_id,
tmp->to_proc ? tmp->to_proc->pid : 0,
tmp->to_thread ?
tmp->to_thread->pid : 0);
+ spin_unlock(&tmp->lock);
+ binder_inner_proc_unlock(proc);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EPROTO;
+ return_error_line = __LINE__;
goto err_bad_call_stack;
}
while (tmp) {
- if (tmp->from && tmp->from->proc == target_proc)
- target_thread = tmp->from;
+ struct binder_thread *from;
+
+ spin_lock(&tmp->lock);
+ from = tmp->from;
+ if (from && from->proc == target_proc) {
+ atomic_inc(&from->tmp_ref);
+ target_thread = from;
+ spin_unlock(&tmp->lock);
+ break;
+ }
+ spin_unlock(&tmp->lock);
tmp = tmp->from_parent;
}
}
+ binder_inner_proc_unlock(proc);
}
- if (target_thread) {
+ if (target_thread)
e->to_thread = target_thread->pid;
- target_list = &target_thread->todo;
- target_wait = &target_thread->wait;
- } else {
- target_list = &target_proc->todo;
- target_wait = &target_proc->wait;
- }
e->to_proc = target_proc->pid;
/* TODO: reuse incoming transaction for reply */
- t = kzalloc_preempt_disabled(sizeof(*t));
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
return_error = BR_FAILED_REPLY;
+ return_error_param = -ENOMEM;
+ return_error_line = __LINE__;
goto err_alloc_t_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION);
+ spin_lock_init(&t->lock);
- tcomplete = kzalloc_preempt_disabled(sizeof(*tcomplete));
+ tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
if (tcomplete == NULL) {
return_error = BR_FAILED_REPLY;
+ return_error_param = -ENOMEM;
+ return_error_line = __LINE__;
goto err_alloc_tcomplete_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
- t->debug_id = atomic_inc_return(&binder_last_id);
- e->debug_id = t->debug_id;
+ t->debug_id = t_debug_id;
if (reply)
binder_debug(BINDER_DEBUG_TRANSACTION,
@@ -2084,15 +2981,30 @@ static void binder_transaction(struct binder_proc *proc,
t->to_thread = target_thread;
t->code = tr->code;
t->flags = tr->flags;
- t->priority = task_nice(current);
+ if (!(t->flags & TF_ONE_WAY) &&
+ binder_supported_policy(current->policy)) {
+ /* Inherit supported policies for synchronous transactions */
+ t->priority.sched_policy = current->policy;
+ t->priority.prio = current->normal_prio;
+ } else {
+ /* Otherwise, fall back to the default priority */
+ t->priority = target_proc->default_priority;
+ }
trace_binder_transaction(reply, t, target_node);
- t->buffer = binder_alloc_buf(target_proc, tr->data_size,
+ t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY));
- if (t->buffer == NULL) {
- return_error = BR_FAILED_REPLY;
+ if (IS_ERR(t->buffer)) {
+ /*
+ * -ESRCH indicates VMA cleared. The target is dying.
+ */
+ return_error_param = PTR_ERR(t->buffer);
+ return_error = return_error_param == -ESRCH ?
+ BR_DEAD_REPLY : BR_FAILED_REPLY;
+ return_error_line = __LINE__;
+ t->buffer = NULL;
goto err_binder_alloc_buf_failed;
}
t->buffer->allow_user_free = 0;
@@ -2100,31 +3012,34 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer->transaction = t;
t->buffer->target_node = target_node;
trace_binder_transaction_alloc_buf(t->buffer);
- if (target_node)
- binder_inc_node(target_node, 1, 0, NULL);
-
off_start = (binder_size_t *)(t->buffer->data +
ALIGN(tr->data_size, sizeof(void *)));
offp = off_start;
- if (copy_from_user_preempt_disabled(t->buffer->data, (const void __user *)(uintptr_t)
+ if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EFAULT;
+ return_error_line = __LINE__;
goto err_copy_data_failed;
}
- if (copy_from_user_preempt_disabled(offp, (const void __user *)(uintptr_t)
+ if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EFAULT;
+ return_error_line = __LINE__;
goto err_copy_data_failed;
}
if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) {
binder_user_error("%d:%d got transaction with invalid offsets size, %lld\n",
proc->pid, thread->pid, (u64)tr->offsets_size);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
goto err_bad_offset;
}
if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
@@ -2132,6 +3047,8 @@ static void binder_transaction(struct binder_proc *proc,
proc->pid, thread->pid,
(u64)extra_buffers_size);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
goto err_bad_offset;
}
off_end = (void *)off_start + tr->offsets_size;
@@ -2148,6 +3065,8 @@ static void binder_transaction(struct binder_proc *proc,
(u64)off_min,
(u64)t->buffer->data_size);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
goto err_bad_offset;
}
@@ -2162,6 +3081,8 @@ static void binder_transaction(struct binder_proc *proc,
ret = binder_translate_binder(fp, t, thread);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
+ return_error_param = ret;
+ return_error_line = __LINE__;
goto err_translate_failed;
}
} break;
@@ -2173,6 +3094,8 @@ static void binder_transaction(struct binder_proc *proc,
ret = binder_translate_handle(fp, t, thread);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
+ return_error_param = ret;
+ return_error_line = __LINE__;
goto err_translate_failed;
}
} break;
@@ -2184,6 +3107,8 @@ static void binder_transaction(struct binder_proc *proc,
if (target_fd < 0) {
return_error = BR_FAILED_REPLY;
+ return_error_param = target_fd;
+ return_error_line = __LINE__;
goto err_translate_failed;
}
fp->pad_binder = 0;
@@ -2200,6 +3125,8 @@ static void binder_transaction(struct binder_proc *proc,
binder_user_error("%d:%d got transaction with invalid parent offset or type\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
goto err_bad_parent;
}
if (!binder_validate_fixup(t->buffer, off_start,
@@ -2209,12 +3136,16 @@ static void binder_transaction(struct binder_proc *proc,
binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
goto err_bad_parent;
}
ret = binder_translate_fd_array(fda, parent, t, thread,
in_reply_to);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
+ return_error_param = ret;
+ return_error_line = __LINE__;
goto err_translate_failed;
}
last_fixup_obj = parent;
@@ -2230,20 +3161,24 @@ static void binder_transaction(struct binder_proc *proc,
binder_user_error("%d:%d got transaction with too large buffer\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
goto err_bad_offset;
}
- if (copy_from_user_preempt_disabled(
- sg_bufp,
- (const void __user *)(uintptr_t)
- bp->buffer, bp->length)) {
+ if (copy_from_user(sg_bufp,
+ (const void __user *)(uintptr_t)
+ bp->buffer, bp->length)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
+ return_error_param = -EFAULT;
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_copy_data_failed;
}
/* Fixup buffer pointer to target proc address space */
bp->buffer = (uintptr_t)sg_bufp +
- target_proc->user_buffer_offset;
+ binder_alloc_get_user_buffer_offset(
+ &target_proc->alloc);
sg_bufp += ALIGN(bp->length, sizeof(u64));
ret = binder_fixup_parent(t, thread, bp, off_start,
@@ -2252,6 +3187,8 @@ static void binder_transaction(struct binder_proc *proc,
last_fixup_min_off);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
+ return_error_param = ret;
+ return_error_line = __LINE__;
goto err_translate_failed;
}
last_fixup_obj = bp;
@@ -2261,42 +3198,61 @@ static void binder_transaction(struct binder_proc *proc,
binder_user_error("%d:%d got transaction with invalid object type, %x\n",
proc->pid, thread->pid, hdr->type);
return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
goto err_bad_object_type;
}
}
+ tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
+ binder_enqueue_work(proc, tcomplete, &thread->todo);
+ t->work.type = BINDER_WORK_TRANSACTION;
+
if (reply) {
+ binder_inner_proc_lock(target_proc);
+ if (target_thread->is_dead) {
+ binder_inner_proc_unlock(target_proc);
+ goto err_dead_proc_or_thread;
+ }
BUG_ON(t->buffer->async_transaction != 0);
- binder_pop_transaction(target_thread, in_reply_to);
+ binder_pop_transaction_ilocked(target_thread, in_reply_to);
+ binder_enqueue_work_ilocked(&t->work, &target_thread->todo);
+ binder_inner_proc_unlock(target_proc);
+ wake_up_interruptible_sync(&target_thread->wait);
+ binder_restore_priority(current, in_reply_to->saved_priority);
+ binder_free_transaction(in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {
BUG_ON(t->buffer->async_transaction != 0);
+ binder_inner_proc_lock(proc);
t->need_reply = 1;
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
+ binder_inner_proc_unlock(proc);
+ if (!binder_proc_transaction(t, target_proc, target_thread)) {
+ binder_inner_proc_lock(proc);
+ binder_pop_transaction_ilocked(thread, t);
+ binder_inner_proc_unlock(proc);
+ goto err_dead_proc_or_thread;
+ }
} else {
BUG_ON(target_node == NULL);
BUG_ON(t->buffer->async_transaction != 1);
- if (target_node->has_async_transaction) {
- target_list = &target_node->async_todo;
- target_wait = NULL;
- } else
- target_node->has_async_transaction = 1;
- }
- t->work.type = BINDER_WORK_TRANSACTION;
- list_add_tail(&t->work.entry, target_list);
- tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
- list_add_tail(&tcomplete->entry, &thread->todo);
- if (target_wait) {
- if (reply || !(t->flags & TF_ONE_WAY)) {
- preempt_disable();
- wake_up_interruptible_sync(target_wait);
- preempt_enable_no_resched();
- }
- else {
- wake_up_interruptible(target_wait);
- }
+ if (!binder_proc_transaction(t, target_proc, NULL))
+ goto err_dead_proc_or_thread;
}
+ if (target_thread)
+ binder_thread_dec_tmpref(target_thread);
+ binder_proc_dec_tmpref(target_proc);
+ /*
+ * write barrier to synchronize with initialization
+ * of log entry
+ */
+ smp_wmb();
+ WRITE_ONCE(e->debug_id_done, t_debug_id);
return;
+err_dead_proc_or_thread:
+ return_error = BR_DEAD_REPLY;
+ return_error_line = __LINE__;
err_translate_failed:
err_bad_object_type:
err_bad_offset:
@@ -2304,8 +3260,9 @@ err_bad_parent:
err_copy_data_failed:
trace_binder_transaction_failed_buffer_release(t->buffer);
binder_transaction_buffer_release(target_proc, t->buffer, offp);
+ target_node = NULL;
t->buffer->transaction = NULL;
- binder_free_buf(target_proc, t->buffer);
+ binder_alloc_free_buf(&target_proc->alloc, t->buffer);
err_binder_alloc_buf_failed:
kfree(tcomplete);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
@@ -2318,25 +3275,50 @@ err_empty_call_stack:
err_dead_binder:
err_invalid_target_handle:
err_no_context_mgr_node:
+ if (target_thread)
+ binder_thread_dec_tmpref(target_thread);
+ if (target_proc)
+ binder_proc_dec_tmpref(target_proc);
+ if (target_node)
+ binder_dec_node(target_node, 1, 0);
+
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
- "%d:%d transaction failed %d, size %lld-%lld\n",
- proc->pid, thread->pid, return_error,
- (u64)tr->data_size, (u64)tr->offsets_size);
+ "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n",
+ proc->pid, thread->pid, return_error, return_error_param,
+ (u64)tr->data_size, (u64)tr->offsets_size,
+ return_error_line);
{
struct binder_transaction_log_entry *fe;
- fe = binder_transaction_log_add(
- &context->transaction_log_failed);
+ e->return_error = return_error;
+ e->return_error_param = return_error_param;
+ e->return_error_line = return_error_line;
+ fe = binder_transaction_log_add(&binder_transaction_log_failed);
*fe = *e;
+ /*
+ * write barrier to synchronize with initialization
+ * of log entry
+ */
+ smp_wmb();
+ WRITE_ONCE(e->debug_id_done, t_debug_id);
+ WRITE_ONCE(fe->debug_id_done, t_debug_id);
}
- BUG_ON(thread->return_error != BR_OK);
+ BUG_ON(thread->return_error.cmd != BR_OK);
if (in_reply_to) {
- thread->return_error = BR_TRANSACTION_COMPLETE;
+ binder_restore_priority(current, in_reply_to->saved_priority);
+ thread->return_error.cmd = BR_TRANSACTION_COMPLETE;
+ binder_enqueue_work(thread->proc,
+ &thread->return_error.work,
+ &thread->todo);
binder_send_failed_reply(in_reply_to, return_error);
- } else
- thread->return_error = return_error;
+ } else {
+ thread->return_error.cmd = return_error;
+ binder_enqueue_work(thread->proc,
+ &thread->return_error.work,
+ &thread->todo);
+ }
}
static int binder_thread_write(struct binder_proc *proc,
@@ -2350,15 +3332,17 @@ static int binder_thread_write(struct binder_proc *proc,
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
- while (ptr < end && thread->return_error == BR_OK) {
- if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
+ while (ptr < end && thread->return_error.cmd == BR_OK) {
+ int ret;
+
+ if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
- if (_IOC_NR(cmd) < ARRAY_SIZE(context->binder_stats.bc)) {
- context->binder_stats.bc[_IOC_NR(cmd)]++;
- proc->stats.bc[_IOC_NR(cmd)]++;
- thread->stats.bc[_IOC_NR(cmd)]++;
+ if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
+ atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
+ atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
+ atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
}
switch (cmd) {
case BC_INCREFS:
@@ -2366,53 +3350,61 @@ static int binder_thread_write(struct binder_proc *proc,
case BC_RELEASE:
case BC_DECREFS: {
uint32_t target;
- struct binder_ref *ref;
const char *debug_string;
+ bool strong = cmd == BC_ACQUIRE || cmd == BC_RELEASE;
+ bool increment = cmd == BC_INCREFS || cmd == BC_ACQUIRE;
+ struct binder_ref_data rdata;
- if (get_user_preempt_disabled(target, (uint32_t __user *)ptr))
+ if (get_user(target, (uint32_t __user *)ptr))
return -EFAULT;
+
ptr += sizeof(uint32_t);
- if (target == 0 && context->binder_context_mgr_node &&
- (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
- ref = binder_get_ref_for_node(proc,
- context->binder_context_mgr_node);
- if (ref->desc != target) {
- binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n",
- proc->pid, thread->pid,
- ref->desc);
- }
- } else
- ref = binder_get_ref(proc, target,
- cmd == BC_ACQUIRE ||
- cmd == BC_RELEASE);
- if (ref == NULL) {
- binder_user_error("%d:%d refcount change on invalid ref %d\n",
- proc->pid, thread->pid, target);
- break;
+ ret = -1;
+ if (increment && !target) {
+ struct binder_node *ctx_mgr_node;
+ mutex_lock(&context->context_mgr_node_lock);
+ ctx_mgr_node = context->binder_context_mgr_node;
+ if (ctx_mgr_node)
+ ret = binder_inc_ref_for_node(
+ proc, ctx_mgr_node,
+ strong, NULL, &rdata);
+ mutex_unlock(&context->context_mgr_node_lock);
+ }
+ if (ret)
+ ret = binder_update_ref_for_handle(
+ proc, target, increment, strong,
+ &rdata);
+ if (!ret && rdata.desc != target) {
+ binder_user_error("%d:%d tried to acquire reference to desc %d, got %d instead\n",
+ proc->pid, thread->pid,
+ target, rdata.desc);
}
switch (cmd) {
case BC_INCREFS:
debug_string = "IncRefs";
- binder_inc_ref(ref, 0, NULL);
break;
case BC_ACQUIRE:
debug_string = "Acquire";
- binder_inc_ref(ref, 1, NULL);
break;
case BC_RELEASE:
debug_string = "Release";
- binder_dec_ref(ref, 1);
break;
case BC_DECREFS:
default:
debug_string = "DecRefs";
- binder_dec_ref(ref, 0);
+ break;
+ }
+ if (ret) {
+ binder_user_error("%d:%d %s %d refcount change on invalid ref %d ret %d\n",
+ proc->pid, thread->pid, debug_string,
+ strong, target, ret);
break;
}
binder_debug(BINDER_DEBUG_USER_REFS,
- "%d:%d %s ref %d desc %d s %d w %d for node %d\n",
- proc->pid, thread->pid, debug_string, ref->debug_id,
- ref->desc, ref->strong, ref->weak, ref->node->debug_id);
+ "%d:%d %s ref %d desc %d s %d w %d\n",
+ proc->pid, thread->pid, debug_string,
+ rdata.debug_id, rdata.desc, rdata.strong,
+ rdata.weak);
break;
}
case BC_INCREFS_DONE:
@@ -2420,11 +3412,12 @@ static int binder_thread_write(struct binder_proc *proc,
binder_uintptr_t node_ptr;
binder_uintptr_t cookie;
struct binder_node *node;
+ bool free_node;
- if (get_user_preempt_disabled(node_ptr, (binder_uintptr_t __user *)ptr))
+ if (get_user(node_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
- if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
+ if (get_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
node = binder_get_node(proc, node_ptr);
@@ -2444,13 +3437,17 @@ static int binder_thread_write(struct binder_proc *proc,
"BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
(u64)node_ptr, node->debug_id,
(u64)cookie, (u64)node->cookie);
+ binder_put_node(node);
break;
}
+ binder_node_inner_lock(node);
if (cmd == BC_ACQUIRE_DONE) {
if (node->pending_strong_ref == 0) {
binder_user_error("%d:%d BC_ACQUIRE_DONE node %d has no pending acquire request\n",
proc->pid, thread->pid,
node->debug_id);
+ binder_node_inner_unlock(node);
+ binder_put_node(node);
break;
}
node->pending_strong_ref = 0;
@@ -2459,16 +3456,23 @@ static int binder_thread_write(struct binder_proc *proc,
binder_user_error("%d:%d BC_INCREFS_DONE node %d has no pending increfs request\n",
proc->pid, thread->pid,
node->debug_id);
+ binder_node_inner_unlock(node);
+ binder_put_node(node);
break;
}
node->pending_weak_ref = 0;
}
- binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0);
+ free_node = binder_dec_node_nilocked(node,
+ cmd == BC_ACQUIRE_DONE, 0);
+ WARN_ON(free_node);
binder_debug(BINDER_DEBUG_USER_REFS,
- "%d:%d %s node %d ls %d lw %d\n",
+ "%d:%d %s node %d ls %d lw %d tr %d\n",
proc->pid, thread->pid,
cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
- node->debug_id, node->local_strong_refs, node->local_weak_refs);
+ node->debug_id, node->local_strong_refs,
+ node->local_weak_refs, node->tmp_refs);
+ binder_node_inner_unlock(node);
+ binder_put_node(node);
break;
}
case BC_ATTEMPT_ACQUIRE:
@@ -2482,11 +3486,12 @@ static int binder_thread_write(struct binder_proc *proc,
binder_uintptr_t data_ptr;
struct binder_buffer *buffer;
- if (get_user_preempt_disabled(data_ptr, (binder_uintptr_t __user *)ptr))
+ if (get_user(data_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
- buffer = binder_buffer_lookup(proc, data_ptr);
+ buffer = binder_alloc_prepare_to_free(&proc->alloc,
+ data_ptr);
if (buffer == NULL) {
binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n",
proc->pid, thread->pid, (u64)data_ptr);
@@ -2508,15 +3513,27 @@ static int binder_thread_write(struct binder_proc *proc,
buffer->transaction = NULL;
}
if (buffer->async_transaction && buffer->target_node) {
- BUG_ON(!buffer->target_node->has_async_transaction);
- if (list_empty(&buffer->target_node->async_todo))
- buffer->target_node->has_async_transaction = 0;
- else
- list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
+ struct binder_node *buf_node;
+ struct binder_work *w;
+
+ buf_node = buffer->target_node;
+ binder_node_inner_lock(buf_node);
+ BUG_ON(!buf_node->has_async_transaction);
+ BUG_ON(buf_node->proc != proc);
+ w = binder_dequeue_work_head_ilocked(
+ &buf_node->async_todo);
+ if (!w) {
+ buf_node->has_async_transaction = 0;
+ } else {
+ binder_enqueue_work_ilocked(
+ w, &proc->todo);
+ binder_wakeup_proc_ilocked(proc);
+ }
+ binder_node_inner_unlock(buf_node);
}
trace_binder_transaction_buffer_release(buffer);
binder_transaction_buffer_release(proc, buffer, NULL);
- binder_free_buf(proc, buffer);
+ binder_alloc_free_buf(&proc->alloc, buffer);
break;
}
@@ -2524,8 +3541,7 @@ static int binder_thread_write(struct binder_proc *proc,
case BC_REPLY_SG: {
struct binder_transaction_data_sg tr;
- if (copy_from_user_preempt_disabled(&tr, ptr,
- sizeof(tr)))
+ if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr.transaction_data,
@@ -2536,7 +3552,7 @@ static int binder_thread_write(struct binder_proc *proc,
case BC_REPLY: {
struct binder_transaction_data tr;
- if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
+ if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr,
@@ -2548,6 +3564,7 @@ static int binder_thread_write(struct binder_proc *proc,
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BC_REGISTER_LOOPER\n",
proc->pid, thread->pid);
+ binder_inner_proc_lock(proc);
if (thread->looper & BINDER_LOOPER_STATE_ENTERED) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("%d:%d ERROR: BC_REGISTER_LOOPER called after BC_ENTER_LOOPER\n",
@@ -2561,6 +3578,7 @@ static int binder_thread_write(struct binder_proc *proc,
proc->requested_threads_started++;
}
thread->looper |= BINDER_LOOPER_STATE_REGISTERED;
+ binder_inner_proc_unlock(proc);
break;
case BC_ENTER_LOOPER:
binder_debug(BINDER_DEBUG_THREADS,
@@ -2585,15 +3603,37 @@ static int binder_thread_write(struct binder_proc *proc,
uint32_t target;
binder_uintptr_t cookie;
struct binder_ref *ref;
- struct binder_ref_death *death;
+ struct binder_ref_death *death = NULL;
- if (get_user_preempt_disabled(target, (uint32_t __user *)ptr))
+ if (get_user(target, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
+ if (get_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
- ref = binder_get_ref(proc, target, false);
+ if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
+ /*
+ * Allocate memory for death notification
+ * before taking lock
+ */
+ death = kzalloc(sizeof(*death), GFP_KERNEL);
+ if (death == NULL) {
+ WARN_ON(thread->return_error.cmd !=
+ BR_OK);
+ thread->return_error.cmd = BR_ERROR;
+ binder_enqueue_work(
+ thread->proc,
+ &thread->return_error.work,
+ &thread->todo);
+ binder_debug(
+ BINDER_DEBUG_FAILED_TRANSACTION,
+ "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n",
+ proc->pid, thread->pid);
+ break;
+ }
+ }
+ binder_proc_lock(proc);
+ ref = binder_get_ref_olocked(proc, target, false);
if (ref == NULL) {
binder_user_error("%d:%d %s invalid ref %d\n",
proc->pid, thread->pid,
@@ -2601,6 +3641,8 @@ static int binder_thread_write(struct binder_proc *proc,
"BC_REQUEST_DEATH_NOTIFICATION" :
"BC_CLEAR_DEATH_NOTIFICATION",
target);
+ binder_proc_unlock(proc);
+ kfree(death);
break;
}
@@ -2610,21 +3652,18 @@ static int binder_thread_write(struct binder_proc *proc,
cmd == BC_REQUEST_DEATH_NOTIFICATION ?
"BC_REQUEST_DEATH_NOTIFICATION" :
"BC_CLEAR_DEATH_NOTIFICATION",
- (u64)cookie, ref->debug_id, ref->desc,
- ref->strong, ref->weak, ref->node->debug_id);
+ (u64)cookie, ref->data.debug_id,
+ ref->data.desc, ref->data.strong,
+ ref->data.weak, ref->node->debug_id);
+ binder_node_lock(ref->node);
if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
if (ref->death) {
binder_user_error("%d:%d BC_REQUEST_DEATH_NOTIFICATION death notification already set\n",
proc->pid, thread->pid);
- break;
- }
- death = kzalloc_preempt_disabled(sizeof(*death));
- if (death == NULL) {
- thread->return_error = BR_ERROR;
- binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
- "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n",
- proc->pid, thread->pid);
+ binder_node_unlock(ref->node);
+ binder_proc_unlock(proc);
+ kfree(death);
break;
}
binder_stats_created(BINDER_STAT_DEATH);
@@ -2633,17 +3672,19 @@ static int binder_thread_write(struct binder_proc *proc,
ref->death = death;
if (ref->node->proc == NULL) {
ref->death->work.type = BINDER_WORK_DEAD_BINDER;
- if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
- list_add_tail(&ref->death->work.entry, &thread->todo);
- } else {
- list_add_tail(&ref->death->work.entry, &proc->todo);
- wake_up_interruptible(&proc->wait);
- }
+
+ binder_inner_proc_lock(proc);
+ binder_enqueue_work_ilocked(
+ &ref->death->work, &proc->todo);
+ binder_wakeup_proc_ilocked(proc);
+ binder_inner_proc_unlock(proc);
}
} else {
if (ref->death == NULL) {
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification not active\n",
proc->pid, thread->pid);
+ binder_node_unlock(ref->node);
+ binder_proc_unlock(proc);
break;
}
death = ref->death;
@@ -2652,33 +3693,52 @@ static int binder_thread_write(struct binder_proc *proc,
proc->pid, thread->pid,
(u64)death->cookie,
(u64)cookie);
+ binder_node_unlock(ref->node);
+ binder_proc_unlock(proc);
break;
}
ref->death = NULL;
+ binder_inner_proc_lock(proc);
if (list_empty(&death->work.entry)) {
death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
- if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
- list_add_tail(&death->work.entry, &thread->todo);
- } else {
- list_add_tail(&death->work.entry, &proc->todo);
- wake_up_interruptible(&proc->wait);
+ if (thread->looper &
+ (BINDER_LOOPER_STATE_REGISTERED |
+ BINDER_LOOPER_STATE_ENTERED))
+ binder_enqueue_work_ilocked(
+ &death->work,
+ &thread->todo);
+ else {
+ binder_enqueue_work_ilocked(
+ &death->work,
+ &proc->todo);
+ binder_wakeup_proc_ilocked(
+ proc);
}
} else {
BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER);
death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;
}
+ binder_inner_proc_unlock(proc);
}
+ binder_node_unlock(ref->node);
+ binder_proc_unlock(proc);
} break;
case BC_DEAD_BINDER_DONE: {
struct binder_work *w;
binder_uintptr_t cookie;
struct binder_ref_death *death = NULL;
- if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
+
+ if (get_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(cookie);
- list_for_each_entry(w, &proc->delivered_death, entry) {
- struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work);
+ binder_inner_proc_lock(proc);
+ list_for_each_entry(w, &proc->delivered_death,
+ entry) {
+ struct binder_ref_death *tmp_death =
+ container_of(w,
+ struct binder_ref_death,
+ work);
if (tmp_death->cookie == cookie) {
death = tmp_death;
@@ -2692,21 +3752,26 @@ static int binder_thread_write(struct binder_proc *proc,
if (death == NULL) {
binder_user_error("%d:%d BC_DEAD_BINDER_DONE %016llx not found\n",
proc->pid, thread->pid, (u64)cookie);
+ binder_inner_proc_unlock(proc);
break;
}
-
- list_del_init(&death->work.entry);
+ binder_dequeue_work_ilocked(&death->work);
if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) {
death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
- if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
- list_add_tail(&death->work.entry, &thread->todo);
- } else {
- list_add_tail(&death->work.entry, &proc->todo);
- wake_up_interruptible(&proc->wait);
+ if (thread->looper &
+ (BINDER_LOOPER_STATE_REGISTERED |
+ BINDER_LOOPER_STATE_ENTERED))
+ binder_enqueue_work_ilocked(
+ &death->work, &thread->todo);
+ else {
+ binder_enqueue_work_ilocked(
+ &death->work,
+ &proc->todo);
+ binder_wakeup_proc_ilocked(proc);
}
}
- }
- break;
+ binder_inner_proc_unlock(proc);
+ } break;
default:
pr_err("%d:%d unknown command %d\n",
@@ -2722,24 +3787,74 @@ static void binder_stat_br(struct binder_proc *proc,
struct binder_thread *thread, uint32_t cmd)
{
trace_binder_return(cmd);
- if (_IOC_NR(cmd) < ARRAY_SIZE(proc->stats.br)) {
- proc->context->binder_stats.br[_IOC_NR(cmd)]++;
- proc->stats.br[_IOC_NR(cmd)]++;
- thread->stats.br[_IOC_NR(cmd)]++;
+ if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) {
+ atomic_inc(&binder_stats.br[_IOC_NR(cmd)]);
+ atomic_inc(&proc->stats.br[_IOC_NR(cmd)]);
+ atomic_inc(&thread->stats.br[_IOC_NR(cmd)]);
}
}
-static int binder_has_proc_work(struct binder_proc *proc,
- struct binder_thread *thread)
+static int binder_put_node_cmd(struct binder_proc *proc,
+ struct binder_thread *thread,
+ void __user **ptrp,
+ binder_uintptr_t node_ptr,
+ binder_uintptr_t node_cookie,
+ int node_debug_id,
+ uint32_t cmd, const char *cmd_name)
{
- return !list_empty(&proc->todo) ||
- (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
+ void __user *ptr = *ptrp;
+
+ if (put_user(cmd, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+
+ if (put_user(node_ptr, (binder_uintptr_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(binder_uintptr_t);
+
+ if (put_user(node_cookie, (binder_uintptr_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(binder_uintptr_t);
+
+ binder_stat_br(proc, thread, cmd);
+ binder_debug(BINDER_DEBUG_USER_REFS, "%d:%d %s %d u%016llx c%016llx\n",
+ proc->pid, thread->pid, cmd_name, node_debug_id,
+ (u64)node_ptr, (u64)node_cookie);
+
+ *ptrp = ptr;
+ return 0;
}
-static int binder_has_thread_work(struct binder_thread *thread)
+static int binder_wait_for_work(struct binder_thread *thread,
+ bool do_proc_work)
{
- return !list_empty(&thread->todo) || thread->return_error != BR_OK ||
- (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
+ DEFINE_WAIT(wait);
+ struct binder_proc *proc = thread->proc;
+ int ret = 0;
+
+ freezer_do_not_count();
+ binder_inner_proc_lock(proc);
+ for (;;) {
+ prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);
+ if (binder_has_work_ilocked(thread, do_proc_work))
+ break;
+ if (do_proc_work)
+ list_add(&thread->waiting_thread_node,
+ &proc->waiting_threads);
+ binder_inner_proc_unlock(proc);
+ schedule();
+ binder_inner_proc_lock(proc);
+ list_del_init(&thread->waiting_thread_node);
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ }
+ finish_wait(&thread->wait, &wait);
+ binder_inner_proc_unlock(proc);
+ freezer_count();
+
+ return ret;
}
static int binder_thread_read(struct binder_proc *proc,
@@ -2755,43 +3870,21 @@ static int binder_thread_read(struct binder_proc *proc,
int wait_for_proc_work;
if (*consumed == 0) {
- if (put_user_preempt_disabled(BR_NOOP, (uint32_t __user *)ptr))
+ if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}
retry:
- wait_for_proc_work = thread->transaction_stack == NULL &&
- list_empty(&thread->todo);
-
- if (thread->return_error != BR_OK && ptr < end) {
- if (thread->return_error2 != BR_OK) {
- if (put_user_preempt_disabled(thread->return_error2, (uint32_t __user *)ptr))
- return -EFAULT;
- ptr += sizeof(uint32_t);
- binder_stat_br(proc, thread, thread->return_error2);
- if (ptr == end)
- goto done;
- thread->return_error2 = BR_OK;
- }
- if (put_user_preempt_disabled(thread->return_error, (uint32_t __user *)ptr))
- return -EFAULT;
- ptr += sizeof(uint32_t);
- binder_stat_br(proc, thread, thread->return_error);
- thread->return_error = BR_OK;
- goto done;
- }
-
+ binder_inner_proc_lock(proc);
+ wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
+ binder_inner_proc_unlock(proc);
thread->looper |= BINDER_LOOPER_STATE_WAITING;
- if (wait_for_proc_work)
- proc->ready_threads++;
-
- binder_unlock(proc->context, __func__);
trace_binder_wait_for_work(wait_for_proc_work,
!!thread->transaction_stack,
- !list_empty(&thread->todo));
+ !binder_worklist_empty(proc, &thread->todo));
if (wait_for_proc_work) {
if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))) {
@@ -2800,24 +3893,16 @@ retry:
wait_event_interruptible(binder_user_error_wait,
binder_stop_on_user_error < 2);
}
- binder_set_nice(proc->default_priority);
- if (non_block) {
- if (!binder_has_proc_work(proc, thread))
- ret = -EAGAIN;
- } else
- ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
- } else {
- if (non_block) {
- if (!binder_has_thread_work(thread))
- ret = -EAGAIN;
- } else
- ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
+ binder_restore_priority(current, proc->default_priority);
}
- binder_lock(proc->context, __func__);
+ if (non_block) {
+ if (!binder_has_work(thread, wait_for_proc_work))
+ ret = -EAGAIN;
+ } else {
+ ret = binder_wait_for_work(thread, wait_for_proc_work);
+ }
- if (wait_for_proc_work)
- proc->ready_threads--;
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
if (ret)
@@ -2826,33 +3911,54 @@ retry:
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
- struct binder_work *w;
+ struct binder_work *w = NULL;
+ struct list_head *list = NULL;
struct binder_transaction *t = NULL;
+ struct binder_thread *t_from;
+
+ binder_inner_proc_lock(proc);
+ if (!binder_worklist_empty_ilocked(&thread->todo))
+ list = &thread->todo;
+ else if (!binder_worklist_empty_ilocked(&proc->todo) &&
+ wait_for_proc_work)
+ list = &proc->todo;
+ else {
+ binder_inner_proc_unlock(proc);
- if (!list_empty(&thread->todo)) {
- w = list_first_entry(&thread->todo, struct binder_work,
- entry);
- } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
- w = list_first_entry(&proc->todo, struct binder_work,
- entry);
- } else {
/* no data added */
- if (ptr - buffer == 4 &&
- !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
+ if (ptr - buffer == 4 && !thread->looper_need_return)
goto retry;
break;
}
- if (end - ptr < sizeof(tr) + 4)
+ if (end - ptr < sizeof(tr) + 4) {
+ binder_inner_proc_unlock(proc);
break;
+ }
+ w = binder_dequeue_work_head_ilocked(list);
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
+ binder_inner_proc_unlock(proc);
t = container_of(w, struct binder_transaction, work);
} break;
+ case BINDER_WORK_RETURN_ERROR: {
+ struct binder_error *e = container_of(
+ w, struct binder_error, work);
+
+ WARN_ON(e->cmd == BR_OK);
+ binder_inner_proc_unlock(proc);
+ if (put_user(e->cmd, (uint32_t __user *)ptr))
+ return -EFAULT;
+ e->cmd = BR_OK;
+ ptr += sizeof(uint32_t);
+
+ binder_stat_br(proc, thread, cmd);
+ } break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
+ binder_inner_proc_unlock(proc);
cmd = BR_TRANSACTION_COMPLETE;
- if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
+ if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
@@ -2860,112 +3966,134 @@ retry:
binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE,
"%d:%d BR_TRANSACTION_COMPLETE\n",
proc->pid, thread->pid);
-
- list_del(&w->entry);
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
case BINDER_WORK_NODE: {
struct binder_node *node = container_of(w, struct binder_node, work);
- uint32_t cmd = BR_NOOP;
- const char *cmd_name;
- int strong = node->internal_strong_refs || node->local_strong_refs;
- int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong;
-
- if (weak && !node->has_weak_ref) {
- cmd = BR_INCREFS;
- cmd_name = "BR_INCREFS";
+ int strong, weak;
+ binder_uintptr_t node_ptr = node->ptr;
+ binder_uintptr_t node_cookie = node->cookie;
+ int node_debug_id = node->debug_id;
+ int has_weak_ref;
+ int has_strong_ref;
+ void __user *orig_ptr = ptr;
+
+ BUG_ON(proc != node->proc);
+ strong = node->internal_strong_refs ||
+ node->local_strong_refs;
+ weak = !hlist_empty(&node->refs) ||
+ node->local_weak_refs ||
+ node->tmp_refs || strong;
+ has_strong_ref = node->has_strong_ref;
+ has_weak_ref = node->has_weak_ref;
+
+ if (weak && !has_weak_ref) {
node->has_weak_ref = 1;
node->pending_weak_ref = 1;
node->local_weak_refs++;
- } else if (strong && !node->has_strong_ref) {
- cmd = BR_ACQUIRE;
- cmd_name = "BR_ACQUIRE";
+ }
+ if (strong && !has_strong_ref) {
node->has_strong_ref = 1;
node->pending_strong_ref = 1;
node->local_strong_refs++;
- } else if (!strong && node->has_strong_ref) {
- cmd = BR_RELEASE;
- cmd_name = "BR_RELEASE";
+ }
+ if (!strong && has_strong_ref)
node->has_strong_ref = 0;
- } else if (!weak && node->has_weak_ref) {
- cmd = BR_DECREFS;
- cmd_name = "BR_DECREFS";
+ if (!weak && has_weak_ref)
node->has_weak_ref = 0;
- }
- if (cmd != BR_NOOP) {
- if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
- return -EFAULT;
- ptr += sizeof(uint32_t);
- if (put_user_preempt_disabled(node->ptr, (binder_uintptr_t __user *)
- (binder_uintptr_t __user *)ptr))
- return -EFAULT;
- ptr += sizeof(binder_uintptr_t);
- if (put_user_preempt_disabled(node->cookie, (binder_uintptr_t __user *)
- (binder_uintptr_t __user *)ptr))
- return -EFAULT;
- ptr += sizeof(binder_uintptr_t);
-
- binder_stat_br(proc, thread, cmd);
- binder_debug(BINDER_DEBUG_USER_REFS,
- "%d:%d %s %d u%016llx c%016llx\n",
- proc->pid, thread->pid, cmd_name,
- node->debug_id,
- (u64)node->ptr, (u64)node->cookie);
- } else {
- list_del_init(&w->entry);
- if (!weak && !strong) {
- binder_debug(BINDER_DEBUG_INTERNAL_REFS,
- "%d:%d node %d u%016llx c%016llx deleted\n",
- proc->pid, thread->pid,
- node->debug_id,
- (u64)node->ptr,
- (u64)node->cookie);
- rb_erase(&node->rb_node, &proc->nodes);
- kfree(node);
- binder_stats_deleted(BINDER_STAT_NODE);
- } else {
- binder_debug(BINDER_DEBUG_INTERNAL_REFS,
- "%d:%d node %d u%016llx c%016llx state unchanged\n",
- proc->pid, thread->pid,
- node->debug_id,
- (u64)node->ptr,
- (u64)node->cookie);
- }
- }
+ if (!weak && !strong) {
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "%d:%d node %d u%016llx c%016llx deleted\n",
+ proc->pid, thread->pid,
+ node_debug_id,
+ (u64)node_ptr,
+ (u64)node_cookie);
+ rb_erase(&node->rb_node, &proc->nodes);
+ binder_inner_proc_unlock(proc);
+ binder_node_lock(node);
+ /*
+ * Acquire the node lock before freeing the
+ * node to serialize with other threads that
+ * may have been holding the node lock while
+ * decrementing this node (avoids race where
+ * this thread frees while the other thread
+ * is unlocking the node after the final
+ * decrement)
+ */
+ binder_node_unlock(node);
+ binder_free_node(node);
+ } else
+ binder_inner_proc_unlock(proc);
+
+ if (weak && !has_weak_ref)
+ ret = binder_put_node_cmd(
+ proc, thread, &ptr, node_ptr,
+ node_cookie, node_debug_id,
+ BR_INCREFS, "BR_INCREFS");
+ if (!ret && strong && !has_strong_ref)
+ ret = binder_put_node_cmd(
+ proc, thread, &ptr, node_ptr,
+ node_cookie, node_debug_id,
+ BR_ACQUIRE, "BR_ACQUIRE");
+ if (!ret && !strong && has_strong_ref)
+ ret = binder_put_node_cmd(
+ proc, thread, &ptr, node_ptr,
+ node_cookie, node_debug_id,
+ BR_RELEASE, "BR_RELEASE");
+ if (!ret && !weak && has_weak_ref)
+ ret = binder_put_node_cmd(
+ proc, thread, &ptr, node_ptr,
+ node_cookie, node_debug_id,
+ BR_DECREFS, "BR_DECREFS");
+ if (orig_ptr == ptr)
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "%d:%d node %d u%016llx c%016llx state unchanged\n",
+ proc->pid, thread->pid,
+ node_debug_id,
+ (u64)node_ptr,
+ (u64)node_cookie);
+ if (ret)
+ return ret;
} break;
case BINDER_WORK_DEAD_BINDER:
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
struct binder_ref_death *death;
uint32_t cmd;
+ binder_uintptr_t cookie;
death = container_of(w, struct binder_ref_death, work);
if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
else
cmd = BR_DEAD_BINDER;
- if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
- return -EFAULT;
- ptr += sizeof(uint32_t);
- if (put_user_preempt_disabled(death->cookie, (binder_uintptr_t __user *) ptr))
- return -EFAULT;
- ptr += sizeof(binder_uintptr_t);
- binder_stat_br(proc, thread, cmd);
+ cookie = death->cookie;
+
binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
"%d:%d %s %016llx\n",
proc->pid, thread->pid,
cmd == BR_DEAD_BINDER ?
"BR_DEAD_BINDER" :
"BR_CLEAR_DEATH_NOTIFICATION_DONE",
- (u64)death->cookie);
-
+ (u64)cookie);
if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
- list_del(&w->entry);
+ binder_inner_proc_unlock(proc);
kfree(death);
binder_stats_deleted(BINDER_STAT_DEATH);
- } else
- list_move(&w->entry, &proc->delivered_death);
+ } else {
+ binder_enqueue_work_ilocked(
+ w, &proc->delivered_death);
+ binder_inner_proc_unlock(proc);
+ }
+ if (put_user(cmd, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (put_user(cookie,
+ (binder_uintptr_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(binder_uintptr_t);
+ binder_stat_br(proc, thread, cmd);
if (cmd == BR_DEAD_BINDER)
goto done; /* DEAD_BINDER notifications can cause transactions */
} break;
@@ -2977,16 +4105,14 @@ retry:
BUG_ON(t->buffer == NULL);
if (t->buffer->target_node) {
struct binder_node *target_node = t->buffer->target_node;
+ struct binder_priority node_prio;
tr.target.ptr = target_node->ptr;
tr.cookie = target_node->cookie;
- t->saved_priority = task_nice(current);
- if (t->priority < target_node->min_priority &&
- !(t->flags & TF_ONE_WAY))
- binder_set_nice(t->priority);
- else if (!(t->flags & TF_ONE_WAY) ||
- t->saved_priority > target_node->min_priority)
- binder_set_nice(target_node->min_priority);
+ node_prio.sched_policy = target_node->sched_policy;
+ node_prio.prio = target_node->min_priority;
+ binder_transaction_priority(current, t, node_prio,
+ target_node->inherit_rt);
cmd = BR_TRANSACTION;
} else {
tr.target.ptr = 0;
@@ -2997,8 +4123,9 @@ retry:
tr.flags = t->flags;
tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
- if (t->from) {
- struct task_struct *sender = t->from->proc->tsk;
+ t_from = binder_get_txn_from(t);
+ if (t_from) {
+ struct task_struct *sender = t_from->proc->tsk;
tr.sender_pid = task_tgid_nr_ns(sender,
task_active_pid_ns(current));
@@ -3008,18 +4135,24 @@ retry:
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
- tr.data.ptr.buffer = (binder_uintptr_t)(
- (uintptr_t)t->buffer->data +
- proc->user_buffer_offset);
+ tr.data.ptr.buffer = (binder_uintptr_t)
+ ((uintptr_t)t->buffer->data +
+ binder_alloc_get_user_buffer_offset(&proc->alloc));
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
- if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
+ if (put_user(cmd, (uint32_t __user *)ptr)) {
+ if (t_from)
+ binder_thread_dec_tmpref(t_from);
return -EFAULT;
+ }
ptr += sizeof(uint32_t);
- if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
+ if (copy_to_user(ptr, &tr, sizeof(tr))) {
+ if (t_from)
+ binder_thread_dec_tmpref(t_from);
return -EFAULT;
+ }
ptr += sizeof(tr);
trace_binder_transaction_received(t);
@@ -3029,21 +4162,22 @@ retry:
proc->pid, thread->pid,
(cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
"BR_REPLY",
- t->debug_id, t->from ? t->from->proc->pid : 0,
- t->from ? t->from->pid : 0, cmd,
+ t->debug_id, t_from ? t_from->proc->pid : 0,
+ t_from ? t_from->pid : 0, cmd,
t->buffer->data_size, t->buffer->offsets_size,
(u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets);
- list_del(&t->work.entry);
+ if (t_from)
+ binder_thread_dec_tmpref(t_from);
t->buffer->allow_user_free = 1;
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
+ binder_inner_proc_lock(thread->proc);
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t;
+ binder_inner_proc_unlock(thread->proc);
} else {
- t->buffer->transaction = NULL;
- kfree(t);
- binder_stats_deleted(BINDER_STAT_TRANSACTION);
+ binder_free_transaction(t);
}
break;
}
@@ -3051,29 +4185,36 @@ retry:
done:
*consumed = ptr - buffer;
- if (proc->requested_threads + proc->ready_threads == 0 &&
+ binder_inner_proc_lock(proc);
+ if (proc->requested_threads == 0 &&
+ list_empty(&thread->proc->waiting_threads) &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
/*spawn a new thread if we leave this out */) {
proc->requested_threads++;
+ binder_inner_proc_unlock(proc);
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BR_SPAWN_LOOPER\n",
proc->pid, thread->pid);
- if (put_user_preempt_disabled(BR_SPAWN_LOOPER, (uint32_t __user *) buffer))
+ if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
- }
+ } else
+ binder_inner_proc_unlock(proc);
return 0;
}
-static void binder_release_work(struct list_head *list)
+static void binder_release_work(struct binder_proc *proc,
+ struct list_head *list)
{
struct binder_work *w;
- while (!list_empty(list)) {
- w = list_first_entry(list, struct binder_work, entry);
- list_del_init(&w->entry);
+ while (1) {
+ w = binder_dequeue_work_head(proc, list);
+ if (!w)
+ return;
+
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
struct binder_transaction *t;
@@ -3086,11 +4227,17 @@ static void binder_release_work(struct list_head *list)
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"undelivered transaction %d\n",
t->debug_id);
- t->buffer->transaction = NULL;
- kfree(t);
- binder_stats_deleted(BINDER_STAT_TRANSACTION);
+ binder_free_transaction(t);
}
} break;
+ case BINDER_WORK_RETURN_ERROR: {
+ struct binder_error *e = container_of(
+ w, struct binder_error, work);
+
+ binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
+ "undelivered TRANSACTION_ERROR: %u\n",
+ e->cmd);
+ } break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"undelivered TRANSACTION_COMPLETE\n");
@@ -3117,7 +4264,8 @@ static void binder_release_work(struct list_head *list)
}
-static struct binder_thread *binder_get_thread(struct binder_proc *proc)
+static struct binder_thread *binder_get_thread_ilocked(
+ struct binder_proc *proc, struct binder_thread *new_thread)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
@@ -3132,38 +4280,102 @@ static struct binder_thread *binder_get_thread(struct binder_proc *proc)
else if (current->pid > thread->pid)
p = &(*p)->rb_right;
else
- break;
+ return thread;
}
- if (*p == NULL) {
- thread = kzalloc_preempt_disabled(sizeof(*thread));
- if (thread == NULL)
+ if (!new_thread)
+ return NULL;
+ thread = new_thread;
+ binder_stats_created(BINDER_STAT_THREAD);
+ thread->proc = proc;
+ thread->pid = current->pid;
+ get_task_struct(current);
+ thread->task = current;
+ atomic_set(&thread->tmp_ref, 0);
+ init_waitqueue_head(&thread->wait);
+ INIT_LIST_HEAD(&thread->todo);
+ rb_link_node(&thread->rb_node, parent, p);
+ rb_insert_color(&thread->rb_node, &proc->threads);
+ thread->looper_need_return = true;
+ thread->return_error.work.type = BINDER_WORK_RETURN_ERROR;
+ thread->return_error.cmd = BR_OK;
+ thread->reply_error.work.type = BINDER_WORK_RETURN_ERROR;
+ thread->reply_error.cmd = BR_OK;
+ INIT_LIST_HEAD(&new_thread->waiting_thread_node);
+ return thread;
+}
+
+static struct binder_thread *binder_get_thread(struct binder_proc *proc)
+{
+ struct binder_thread *thread;
+ struct binder_thread *new_thread;
+
+ binder_inner_proc_lock(proc);
+ thread = binder_get_thread_ilocked(proc, NULL);
+ binder_inner_proc_unlock(proc);
+ if (!thread) {
+ new_thread = kzalloc(sizeof(*thread), GFP_KERNEL);
+ if (new_thread == NULL)
return NULL;
- binder_stats_created(BINDER_STAT_THREAD);
- thread->proc = proc;
- thread->pid = current->pid;
- init_waitqueue_head(&thread->wait);
- INIT_LIST_HEAD(&thread->todo);
- rb_link_node(&thread->rb_node, parent, p);
- rb_insert_color(&thread->rb_node, &proc->threads);
- thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
- thread->return_error = BR_OK;
- thread->return_error2 = BR_OK;
+ binder_inner_proc_lock(proc);
+ thread = binder_get_thread_ilocked(proc, new_thread);
+ binder_inner_proc_unlock(proc);
+ if (thread != new_thread)
+ kfree(new_thread);
}
return thread;
}
-static int binder_free_thread(struct binder_proc *proc,
- struct binder_thread *thread)
+static void binder_free_proc(struct binder_proc *proc)
+{
+ BUG_ON(!list_empty(&proc->todo));
+ BUG_ON(!list_empty(&proc->delivered_death));
+ binder_alloc_deferred_release(&proc->alloc);
+ put_task_struct(proc->tsk);
+ binder_stats_deleted(BINDER_STAT_PROC);
+ kfree(proc);
+}
+
+static void binder_free_thread(struct binder_thread *thread)
+{
+ BUG_ON(!list_empty(&thread->todo));
+ binder_stats_deleted(BINDER_STAT_THREAD);
+ binder_proc_dec_tmpref(thread->proc);
+ put_task_struct(thread->task);
+ kfree(thread);
+}
+
+static int binder_thread_release(struct binder_proc *proc,
+ struct binder_thread *thread)
{
struct binder_transaction *t;
struct binder_transaction *send_reply = NULL;
int active_transactions = 0;
+ struct binder_transaction *last_t = NULL;
+ binder_inner_proc_lock(thread->proc);
+ /*
+ * take a ref on the proc so it survives
+ * after we remove this thread from proc->threads.
+ * The corresponding dec is when we actually
+ * free the thread in binder_free_thread()
+ */
+ proc->tmp_ref++;
+ /*
+ * take a ref on this thread to ensure it
+ * survives while we are releasing it
+ */
+ atomic_inc(&thread->tmp_ref);
rb_erase(&thread->rb_node, &proc->threads);
t = thread->transaction_stack;
- if (t && t->to_thread == thread)
- send_reply = t;
+ if (t) {
+ spin_lock(&t->lock);
+ if (t->to_thread == thread)
+ send_reply = t;
+ }
+ thread->is_dead = true;
+
while (t) {
+ last_t = t;
active_transactions++;
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"release %d:%d transaction %d %s, still active\n",
@@ -3184,12 +4396,16 @@ static int binder_free_thread(struct binder_proc *proc,
t = t->from_parent;
} else
BUG();
+ spin_unlock(&last_t->lock);
+ if (t)
+ spin_lock(&t->lock);
}
+ binder_inner_proc_unlock(thread->proc);
+
if (send_reply)
binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
- binder_release_work(&thread->todo);
- kfree(thread);
- binder_stats_deleted(BINDER_STAT_THREAD);
+ binder_release_work(proc, &thread->todo);
+ binder_thread_dec_tmpref(thread);
return active_transactions;
}
@@ -3198,30 +4414,21 @@ static unsigned int binder_poll(struct file *filp,
{
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread = NULL;
- int wait_for_proc_work;
-
- binder_lock(proc->context, __func__);
+ bool wait_for_proc_work;
thread = binder_get_thread(proc);
- wait_for_proc_work = thread->transaction_stack == NULL &&
- list_empty(&thread->todo) && thread->return_error == BR_OK;
+ binder_inner_proc_lock(thread->proc);
+ thread->looper |= BINDER_LOOPER_STATE_POLL;
+ wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
- binder_unlock(proc->context, __func__);
+ binder_inner_proc_unlock(thread->proc);
+
+ poll_wait(filp, &thread->wait, wait);
+
+ if (binder_has_work(thread, wait_for_proc_work))
+ return POLLIN;
- if (wait_for_proc_work) {
- if (binder_has_proc_work(proc, thread))
- return POLLIN;
- poll_wait(filp, &proc->wait, wait);
- if (binder_has_proc_work(proc, thread))
- return POLLIN;
- } else {
- if (binder_has_thread_work(thread))
- return POLLIN;
- poll_wait(filp, &thread->wait, wait);
- if (binder_has_thread_work(thread))
- return POLLIN;
- }
return 0;
}
@@ -3239,7 +4446,7 @@ static int binder_ioctl_write_read(struct file *filp,
ret = -EINVAL;
goto out;
}
- if (copy_from_user_preempt_disabled(&bwr, ubuf, sizeof(bwr))) {
+ if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
@@ -3257,7 +4464,7 @@ static int binder_ioctl_write_read(struct file *filp,
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
- if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr)))
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
@@ -3268,10 +4475,12 @@ static int binder_ioctl_write_read(struct file *filp,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
- if (!list_empty(&proc->todo))
- wake_up_interruptible(&proc->wait);
+ binder_inner_proc_lock(proc);
+ if (!binder_worklist_empty_ilocked(&proc->todo))
+ binder_wakeup_proc_ilocked(proc);
+ binder_inner_proc_unlock(proc);
if (ret < 0) {
- if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr)))
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
@@ -3281,7 +4490,7 @@ static int binder_ioctl_write_read(struct file *filp,
proc->pid, thread->pid,
(u64)bwr.write_consumed, (u64)bwr.write_size,
(u64)bwr.read_consumed, (u64)bwr.read_size);
- if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) {
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
@@ -3294,9 +4503,10 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
int ret = 0;
struct binder_proc *proc = filp->private_data;
struct binder_context *context = proc->context;
-
+ struct binder_node *new_node;
kuid_t curr_euid = current_euid();
+ mutex_lock(&context->context_mgr_node_lock);
if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
@@ -3317,24 +4527,52 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
} else {
context->binder_context_mgr_uid = curr_euid;
}
- context->binder_context_mgr_node = binder_new_node(proc, 0, 0);
- if (!context->binder_context_mgr_node) {
+ new_node = binder_new_node(proc, NULL);
+ if (!new_node) {
ret = -ENOMEM;
goto out;
}
- context->binder_context_mgr_node->local_weak_refs++;
- context->binder_context_mgr_node->local_strong_refs++;
- context->binder_context_mgr_node->has_strong_ref = 1;
- context->binder_context_mgr_node->has_weak_ref = 1;
+ binder_node_lock(new_node);
+ new_node->local_weak_refs++;
+ new_node->local_strong_refs++;
+ new_node->has_strong_ref = 1;
+ new_node->has_weak_ref = 1;
+ context->binder_context_mgr_node = new_node;
+ binder_node_unlock(new_node);
+ binder_put_node(new_node);
out:
+ mutex_unlock(&context->context_mgr_node_lock);
return ret;
}
+static int binder_ioctl_get_node_debug_info(struct binder_proc *proc,
+ struct binder_node_debug_info *info) {
+ struct rb_node *n;
+ binder_uintptr_t ptr = info->ptr;
+
+ memset(info, 0, sizeof(*info));
+
+ binder_inner_proc_lock(proc);
+ for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) {
+ struct binder_node *node = rb_entry(n, struct binder_node,
+ rb_node);
+ if (node->ptr > ptr) {
+ info->ptr = node->ptr;
+ info->cookie = node->cookie;
+ info->has_strong_ref = node->has_strong_ref;
+ info->has_weak_ref = node->has_weak_ref;
+ break;
+ }
+ }
+ binder_inner_proc_unlock(proc);
+
+ return 0;
+}
+
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
- struct binder_context *context = proc->context;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
@@ -3348,7 +4586,6 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (ret)
goto err_unlocked;
- binder_lock(context, __func__);
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
@@ -3361,12 +4598,19 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (ret)
goto err;
break;
- case BINDER_SET_MAX_THREADS:
- if (copy_from_user_preempt_disabled(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
+ case BINDER_SET_MAX_THREADS: {
+ int max_threads;
+
+ if (copy_from_user(&max_threads, ubuf,
+ sizeof(max_threads))) {
ret = -EINVAL;
goto err;
}
+ binder_inner_proc_lock(proc);
+ proc->max_threads = max_threads;
+ binder_inner_proc_unlock(proc);
break;
+ }
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
@@ -3375,7 +4619,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case BINDER_THREAD_EXIT:
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
proc->pid, thread->pid);
- binder_free_thread(proc, thread);
+ binder_thread_release(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: {
@@ -3385,8 +4629,27 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
ret = -EINVAL;
goto err;
}
- if (put_user_preempt_disabled(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) {
- ret = -EINVAL;
+ if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
+ &ver->protocol_version)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ }
+ case BINDER_GET_NODE_DEBUG_INFO: {
+ struct binder_node_debug_info info;
+
+ if (copy_from_user(&info, ubuf, sizeof(info))) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ ret = binder_ioctl_get_node_debug_info(proc, &info);
+ if (ret < 0)
+ goto err;
+
+ if (copy_to_user(ubuf, &info, sizeof(info))) {
+ ret = -EFAULT;
goto err;
}
break;
@@ -3398,8 +4661,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
ret = 0;
err:
if (thread)
- thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
- binder_unlock(context, __func__);
+ thread->looper_need_return = false;
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
@@ -3428,8 +4690,7 @@ static void binder_vma_close(struct vm_area_struct *vma)
proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
- proc->vma = NULL;
- proc->vma_vm_mm = NULL;
+ binder_alloc_vma_close(&proc->alloc);
binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES);
}
@@ -3447,11 +4708,8 @@ static const struct vm_operations_struct binder_vm_ops = {
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
-
- struct vm_struct *area;
struct binder_proc *proc = filp->private_data;
const char *failure_string;
- struct binder_buffer *buffer;
if (proc->tsk != current->group_leader)
return -EINVAL;
@@ -3460,8 +4718,8 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_end = vma->vm_start + SZ_4M;
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
- "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
- proc->pid, vma->vm_start, vma->vm_end,
+ "%s: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
+ __func__, proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
@@ -3471,77 +4729,15 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
goto err_bad_arg;
}
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
-
- mutex_lock(&proc->context->binder_mmap_lock);
- if (proc->buffer) {
- ret = -EBUSY;
- failure_string = "already mapped";
- goto err_already_mapped;
- }
-
- area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
- if (area == NULL) {
- ret = -ENOMEM;
- failure_string = "get_vm_area";
- goto err_get_vm_area_failed;
- }
- proc->buffer = area->addr;
- proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
- mutex_unlock(&proc->context->binder_mmap_lock);
-
-#ifdef CONFIG_CPU_CACHE_VIPT
- if (cache_is_vipt_aliasing()) {
- while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
- pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
- vma->vm_start += PAGE_SIZE;
- }
- }
-#endif
- proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
- if (proc->pages == NULL) {
- ret = -ENOMEM;
- failure_string = "alloc page array";
- goto err_alloc_pages_failed;
- }
- proc->buffer_size = vma->vm_end - vma->vm_start;
-
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
- /* binder_update_page_range assumes preemption is disabled */
- preempt_disable();
- ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma);
- preempt_enable_no_resched();
- if (ret) {
- ret = -ENOMEM;
- failure_string = "alloc small buf";
- goto err_alloc_small_buf_failed;
- }
- buffer = proc->buffer;
- INIT_LIST_HEAD(&proc->buffers);
- list_add(&buffer->entry, &proc->buffers);
- buffer->free = 1;
- binder_insert_free_buffer(proc, buffer);
- proc->free_async_space = proc->buffer_size / 2;
- barrier();
+ ret = binder_alloc_mmap_handler(&proc->alloc, vma);
+ if (ret)
+ return ret;
proc->files = get_files_struct(current);
- proc->vma = vma;
- proc->vma_vm_mm = vma->vm_mm;
-
- /*pr_info("binder_mmap: %d %lx-%lx maps %p\n",
- proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
return 0;
-err_alloc_small_buf_failed:
- kfree(proc->pages);
- proc->pages = NULL;
-err_alloc_pages_failed:
- mutex_lock(&proc->context->binder_mmap_lock);
- vfree(proc->buffer);
- proc->buffer = NULL;
-err_get_vm_area_failed:
-err_already_mapped:
- mutex_unlock(&proc->context->binder_mmap_lock);
err_bad_arg:
pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
@@ -3559,24 +4755,33 @@ static int binder_open(struct inode *nodp, struct file *filp)
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
+ spin_lock_init(&proc->inner_lock);
+ spin_lock_init(&proc->outer_lock);
get_task_struct(current->group_leader);
proc->tsk = current->group_leader;
INIT_LIST_HEAD(&proc->todo);
- init_waitqueue_head(&proc->wait);
- proc->default_priority = task_nice(current);
+ if (binder_supported_policy(current->policy)) {
+ proc->default_priority.sched_policy = current->policy;
+ proc->default_priority.prio = current->normal_prio;
+ } else {
+ proc->default_priority.sched_policy = SCHED_NORMAL;
+ proc->default_priority.prio = NICE_TO_PRIO(0);
+ }
+
binder_dev = container_of(filp->private_data, struct binder_device,
miscdev);
proc->context = &binder_dev->context;
-
- binder_lock(proc->context, __func__);
+ binder_alloc_init(&proc->alloc);
binder_stats_created(BINDER_STAT_PROC);
- hlist_add_head(&proc->proc_node, &proc->context->binder_procs);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
+ INIT_LIST_HEAD(&proc->waiting_threads);
filp->private_data = proc;
- binder_unlock(proc->context, __func__);
+ mutex_lock(&binder_procs_lock);
+ hlist_add_head(&proc->proc_node, &binder_procs);
+ mutex_unlock(&binder_procs_lock);
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
@@ -3612,16 +4817,17 @@ static void binder_deferred_flush(struct binder_proc *proc)
struct rb_node *n;
int wake_count = 0;
+ binder_inner_proc_lock(proc);
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) {
struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node);
- thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
+ thread->looper_need_return = true;
if (thread->looper & BINDER_LOOPER_STATE_WAITING) {
wake_up_interruptible(&thread->wait);
wake_count++;
}
}
- wake_up_interruptible_all(&proc->wait);
+ binder_inner_proc_unlock(proc);
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"binder_flush: %d woke %d threads\n", proc->pid,
@@ -3641,15 +4847,22 @@ static int binder_release(struct inode *nodp, struct file *filp)
static int binder_node_release(struct binder_node *node, int refs)
{
struct binder_ref *ref;
- struct binder_context *context = node->proc->context;
int death = 0;
+ struct binder_proc *proc = node->proc;
- list_del_init(&node->work.entry);
- binder_release_work(&node->async_todo);
+ binder_release_work(proc, &node->async_todo);
- if (hlist_empty(&node->refs)) {
- kfree(node);
- binder_stats_deleted(BINDER_STAT_NODE);
+ binder_node_lock(node);
+ binder_inner_proc_lock(proc);
+ binder_dequeue_work_ilocked(&node->work);
+ /*
+ * The caller must have taken a temporary ref on the node,
+ */
+ BUG_ON(!node->tmp_refs);
+ if (hlist_empty(&node->refs) && node->tmp_refs == 1) {
+ binder_inner_proc_unlock(proc);
+ binder_node_unlock(node);
+ binder_free_node(node);
return refs;
}
@@ -3657,45 +4870,58 @@ static int binder_node_release(struct binder_node *node, int refs)
node->proc = NULL;
node->local_strong_refs = 0;
node->local_weak_refs = 0;
- hlist_add_head(&node->dead_node, &context->binder_dead_nodes);
+ binder_inner_proc_unlock(proc);
+
+ spin_lock(&binder_dead_nodes_lock);
+ hlist_add_head(&node->dead_node, &binder_dead_nodes);
+ spin_unlock(&binder_dead_nodes_lock);
hlist_for_each_entry(ref, &node->refs, node_entry) {
refs++;
-
- if (!ref->death)
+ /*
+ * Need the node lock to synchronize
+ * with new notification requests and the
+ * inner lock to synchronize with queued
+ * death notifications.
+ */
+ binder_inner_proc_lock(ref->proc);
+ if (!ref->death) {
+ binder_inner_proc_unlock(ref->proc);
continue;
+ }
death++;
- if (list_empty(&ref->death->work.entry)) {
- ref->death->work.type = BINDER_WORK_DEAD_BINDER;
- list_add_tail(&ref->death->work.entry,
- &ref->proc->todo);
- wake_up_interruptible(&ref->proc->wait);
- } else
- BUG();
+ BUG_ON(!list_empty(&ref->death->work.entry));
+ ref->death->work.type = BINDER_WORK_DEAD_BINDER;
+ binder_enqueue_work_ilocked(&ref->death->work,
+ &ref->proc->todo);
+ binder_wakeup_proc_ilocked(ref->proc);
+ binder_inner_proc_unlock(ref->proc);
}
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"node %d now dead, refs %d, death %d\n",
node->debug_id, refs, death);
+ binder_node_unlock(node);
+ binder_put_node(node);
return refs;
}
static void binder_deferred_release(struct binder_proc *proc)
{
- struct binder_transaction *t;
struct binder_context *context = proc->context;
struct rb_node *n;
- int threads, nodes, incoming_refs, outgoing_refs, buffers,
- active_transactions, page_count;
+ int threads, nodes, incoming_refs, outgoing_refs, active_transactions;
- BUG_ON(proc->vma);
BUG_ON(proc->files);
+ mutex_lock(&binder_procs_lock);
hlist_del(&proc->proc_node);
+ mutex_unlock(&binder_procs_lock);
+ mutex_lock(&context->context_mgr_node_lock);
if (context->binder_context_mgr_node &&
context->binder_context_mgr_node->proc == proc) {
binder_debug(BINDER_DEBUG_DEAD_BINDER,
@@ -3703,15 +4929,25 @@ static void binder_deferred_release(struct binder_proc *proc)
__func__, proc->pid);
context->binder_context_mgr_node = NULL;
}
+ mutex_unlock(&context->context_mgr_node_lock);
+ binder_inner_proc_lock(proc);
+ /*
+ * Make sure proc stays alive after we
+ * remove all the threads
+ */
+ proc->tmp_ref++;
+ proc->is_dead = true;
threads = 0;
active_transactions = 0;
while ((n = rb_first(&proc->threads))) {
struct binder_thread *thread;
thread = rb_entry(n, struct binder_thread, rb_node);
+ binder_inner_proc_unlock(proc);
threads++;
- active_transactions += binder_free_thread(proc, thread);
+ active_transactions += binder_thread_release(proc, thread);
+ binder_inner_proc_lock(proc);
}
nodes = 0;
@@ -3721,96 +4957,55 @@ static void binder_deferred_release(struct binder_proc *proc)
node = rb_entry(n, struct binder_node, rb_node);
nodes++;
+ /*
+ * take a temporary ref on the node before
+ * calling binder_node_release() which will either
+ * kfree() the node or call binder_put_node()
+ */
+ binder_inc_node_tmpref_ilocked(node);
rb_erase(&node->rb_node, &proc->nodes);
- incoming_refs = binder_node_release(node,
- incoming_refs);
+ binder_inner_proc_unlock(proc);
+ incoming_refs = binder_node_release(node, incoming_refs);
+ binder_inner_proc_lock(proc);
}
+ binder_inner_proc_unlock(proc);
outgoing_refs = 0;
+ binder_proc_lock(proc);
while ((n = rb_first(&proc->refs_by_desc))) {
struct binder_ref *ref;
ref = rb_entry(n, struct binder_ref, rb_node_desc);
outgoing_refs++;
- binder_delete_ref(ref);
- }
-
- binder_release_work(&proc->todo);
- binder_release_work(&proc->delivered_death);
-
- buffers = 0;
- while ((n = rb_first(&proc->allocated_buffers))) {
- struct binder_buffer *buffer;
-
- buffer = rb_entry(n, struct binder_buffer, rb_node);
-
- t = buffer->transaction;
- if (t) {
- t->buffer = NULL;
- buffer->transaction = NULL;
- pr_err("release proc %d, transaction %d, not freed\n",
- proc->pid, t->debug_id);
- /*BUG();*/
- }
-
- binder_free_buf(proc, buffer);
- buffers++;
+ binder_cleanup_ref_olocked(ref);
+ binder_proc_unlock(proc);
+ binder_free_ref(ref);
+ binder_proc_lock(proc);
}
+ binder_proc_unlock(proc);
- binder_stats_deleted(BINDER_STAT_PROC);
-
- page_count = 0;
- if (proc->pages) {
- int i;
-
- for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) {
- void *page_addr;
-
- if (!proc->pages[i])
- continue;
-
- page_addr = proc->buffer + i * PAGE_SIZE;
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%s: %d: page %d at %p not freed\n",
- __func__, proc->pid, i, page_addr);
- unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
- __free_page(proc->pages[i]);
- page_count++;
- }
- kfree(proc->pages);
- preempt_enable_no_resched();
- vfree(proc->buffer);
- preempt_disable();
- }
-
- put_task_struct(proc->tsk);
+ binder_release_work(proc, &proc->todo);
+ binder_release_work(proc, &proc->delivered_death);
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
- "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d, buffers %d, pages %d\n",
+ "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n",
__func__, proc->pid, threads, nodes, incoming_refs,
- outgoing_refs, active_transactions, buffers, page_count);
+ outgoing_refs, active_transactions);
- kfree(proc);
+ binder_proc_dec_tmpref(proc);
}
static void binder_deferred_func(struct work_struct *work)
{
struct binder_proc *proc;
struct files_struct *files;
- struct binder_context *context =
- container_of(work, struct binder_context, deferred_work);
int defer;
do {
- trace_binder_lock(__func__);
- mutex_lock(&context->binder_main_lock);
- trace_binder_locked(__func__);
-
- mutex_lock(&context->binder_deferred_lock);
- preempt_disable();
- if (!hlist_empty(&context->binder_deferred_list)) {
- proc = hlist_entry(context->binder_deferred_list.first,
+ mutex_lock(&binder_deferred_lock);
+ if (!hlist_empty(&binder_deferred_list)) {
+ proc = hlist_entry(binder_deferred_list.first,
struct binder_proc, deferred_work_node);
hlist_del_init(&proc->deferred_work_node);
defer = proc->deferred_work;
@@ -3819,7 +5014,7 @@ static void binder_deferred_func(struct work_struct *work)
proc = NULL;
defer = 0;
}
- mutex_unlock(&context->binder_deferred_lock);
+ mutex_unlock(&binder_deferred_lock);
files = NULL;
if (defer & BINDER_DEFERRED_PUT_FILES) {
@@ -3834,63 +5029,71 @@ static void binder_deferred_func(struct work_struct *work)
if (defer & BINDER_DEFERRED_RELEASE)
binder_deferred_release(proc); /* frees proc */
- trace_binder_unlock(__func__);
- mutex_unlock(&context->binder_main_lock);
- preempt_enable_no_resched();
if (files)
put_files_struct(files);
} while (proc);
}
+static DECLARE_WORK(binder_deferred_work, binder_deferred_func);
static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
{
- mutex_lock(&proc->context->binder_deferred_lock);
+ mutex_lock(&binder_deferred_lock);
proc->deferred_work |= defer;
if (hlist_unhashed(&proc->deferred_work_node)) {
hlist_add_head(&proc->deferred_work_node,
- &proc->context->binder_deferred_list);
- queue_work(proc->context->binder_deferred_workqueue,
- &proc->context->deferred_work);
+ &binder_deferred_list);
+ queue_work(binder_deferred_workqueue, &binder_deferred_work);
}
- mutex_unlock(&proc->context->binder_deferred_lock);
+ mutex_unlock(&binder_deferred_lock);
}
-static void print_binder_transaction(struct seq_file *m, const char *prefix,
- struct binder_transaction *t)
+static void print_binder_transaction_ilocked(struct seq_file *m,
+ struct binder_proc *proc,
+ const char *prefix,
+ struct binder_transaction *t)
{
+ struct binder_proc *to_proc;
+ struct binder_buffer *buffer = t->buffer;
+
+ spin_lock(&t->lock);
+ to_proc = t->to_proc;
seq_printf(m,
- "%s %d: %p from %d:%d to %d:%d code %x flags %x pri %ld r%d",
+ "%s %d: %p from %d:%d to %d:%d code %x flags %x pri %d:%d r%d",
prefix, t->debug_id, t,
t->from ? t->from->proc->pid : 0,
t->from ? t->from->pid : 0,
- t->to_proc ? t->to_proc->pid : 0,
+ to_proc ? to_proc->pid : 0,
t->to_thread ? t->to_thread->pid : 0,
- t->code, t->flags, t->priority, t->need_reply);
- if (t->buffer == NULL) {
+ t->code, t->flags, t->priority.sched_policy,
+ t->priority.prio, t->need_reply);
+ spin_unlock(&t->lock);
+
+ if (proc != to_proc) {
+ /*
+ * Can only safely deref buffer if we are holding the
+ * correct proc inner lock for this node
+ */
+ seq_puts(m, "\n");
+ return;
+ }
+
+ if (buffer == NULL) {
seq_puts(m, " buffer free\n");
return;
}
- if (t->buffer->target_node)
- seq_printf(m, " node %d",
- t->buffer->target_node->debug_id);
+ if (buffer->target_node)
+ seq_printf(m, " node %d", buffer->target_node->debug_id);
seq_printf(m, " size %zd:%zd data %p\n",
- t->buffer->data_size, t->buffer->offsets_size,
- t->buffer->data);
-}
-
-static void print_binder_buffer(struct seq_file *m, const char *prefix,
- struct binder_buffer *buffer)
-{
- seq_printf(m, "%s %d: %p size %zd:%zd %s\n",
- prefix, buffer->debug_id, buffer->data,
buffer->data_size, buffer->offsets_size,
- buffer->transaction ? "active" : "delivered");
+ buffer->data);
}
-static void print_binder_work(struct seq_file *m, const char *prefix,
- const char *transaction_prefix,
- struct binder_work *w)
+static void print_binder_work_ilocked(struct seq_file *m,
+ struct binder_proc *proc,
+ const char *prefix,
+ const char *transaction_prefix,
+ struct binder_work *w)
{
struct binder_node *node;
struct binder_transaction *t;
@@ -3898,8 +5101,16 @@ static void print_binder_work(struct seq_file *m, const char *prefix,
switch (w->type) {
case BINDER_WORK_TRANSACTION:
t = container_of(w, struct binder_transaction, work);
- print_binder_transaction(m, transaction_prefix, t);
+ print_binder_transaction_ilocked(
+ m, proc, transaction_prefix, t);
break;
+ case BINDER_WORK_RETURN_ERROR: {
+ struct binder_error *e = container_of(
+ w, struct binder_error, work);
+
+ seq_printf(m, "%stransaction error: %u\n",
+ prefix, e->cmd);
+ } break;
case BINDER_WORK_TRANSACTION_COMPLETE:
seq_printf(m, "%stransaction complete\n", prefix);
break;
@@ -3924,40 +5135,46 @@ static void print_binder_work(struct seq_file *m, const char *prefix,
}
}
-static void print_binder_thread(struct seq_file *m,
- struct binder_thread *thread,
- int print_always)
+static void print_binder_thread_ilocked(struct seq_file *m,
+ struct binder_thread *thread,
+ int print_always)
{
struct binder_transaction *t;
struct binder_work *w;
size_t start_pos = m->count;
size_t header_pos;
- seq_printf(m, " thread %d: l %02x\n", thread->pid, thread->looper);
+ seq_printf(m, " thread %d: l %02x need_return %d tr %d\n",
+ thread->pid, thread->looper,
+ thread->looper_need_return,
+ atomic_read(&thread->tmp_ref));
header_pos = m->count;
t = thread->transaction_stack;
while (t) {
if (t->from == thread) {
- print_binder_transaction(m,
- " outgoing transaction", t);
+ print_binder_transaction_ilocked(m, thread->proc,
+ " outgoing transaction", t);
t = t->from_parent;
} else if (t->to_thread == thread) {
- print_binder_transaction(m,
+ print_binder_transaction_ilocked(m, thread->proc,
" incoming transaction", t);
t = t->to_parent;
} else {
- print_binder_transaction(m, " bad transaction", t);
+ print_binder_transaction_ilocked(m, thread->proc,
+ " bad transaction", t);
t = NULL;
}
}
list_for_each_entry(w, &thread->todo, entry) {
- print_binder_work(m, " ", " pending transaction", w);
+ print_binder_work_ilocked(m, thread->proc, " ",
+ " pending transaction", w);
}
if (!print_always && m->count == header_pos)
m->count = start_pos;
}
-static void print_binder_node(struct seq_file *m, struct binder_node *node)
+static void print_binder_node_nilocked(struct seq_file *m,
+ struct binder_node *node)
{
struct binder_ref *ref;
struct binder_work *w;
@@ -3967,27 +5184,35 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node)
hlist_for_each_entry(ref, &node->refs, node_entry)
count++;
- seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d",
+ seq_printf(m, " node %d: u%016llx c%016llx pri %d:%d hs %d hw %d ls %d lw %d is %d iw %d tr %d",
node->debug_id, (u64)node->ptr, (u64)node->cookie,
+ node->sched_policy, node->min_priority,
node->has_strong_ref, node->has_weak_ref,
node->local_strong_refs, node->local_weak_refs,
- node->internal_strong_refs, count);
+ node->internal_strong_refs, count, node->tmp_refs);
if (count) {
seq_puts(m, " proc");
hlist_for_each_entry(ref, &node->refs, node_entry)
seq_printf(m, " %d", ref->proc->pid);
}
seq_puts(m, "\n");
- list_for_each_entry(w, &node->async_todo, entry)
- print_binder_work(m, " ",
- " pending async transaction", w);
+ if (node->proc) {
+ list_for_each_entry(w, &node->async_todo, entry)
+ print_binder_work_ilocked(m, node->proc, " ",
+ " pending async transaction", w);
+ }
}
-static void print_binder_ref(struct seq_file *m, struct binder_ref *ref)
+static void print_binder_ref_olocked(struct seq_file *m,
+ struct binder_ref *ref)
{
+ binder_node_lock(ref->node);
seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n",
- ref->debug_id, ref->desc, ref->node->proc ? "" : "dead ",
- ref->node->debug_id, ref->strong, ref->weak, ref->death);
+ ref->data.debug_id, ref->data.desc,
+ ref->node->proc ? "" : "dead ",
+ ref->node->debug_id, ref->data.strong,
+ ref->data.weak, ref->death);
+ binder_node_unlock(ref->node);
}
static void print_binder_proc(struct seq_file *m,
@@ -3997,36 +5222,60 @@ static void print_binder_proc(struct seq_file *m,
struct rb_node *n;
size_t start_pos = m->count;
size_t header_pos;
+ struct binder_node *last_node = NULL;
seq_printf(m, "proc %d\n", proc->pid);
seq_printf(m, "context %s\n", proc->context->name);
header_pos = m->count;
+ binder_inner_proc_lock(proc);
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
- print_binder_thread(m, rb_entry(n, struct binder_thread,
+ print_binder_thread_ilocked(m, rb_entry(n, struct binder_thread,
rb_node), print_all);
+
for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) {
struct binder_node *node = rb_entry(n, struct binder_node,
rb_node);
- if (print_all || node->has_async_transaction)
- print_binder_node(m, node);
- }
+ /*
+ * take a temporary reference on the node so it
+ * survives and isn't removed from the tree
+ * while we print it.
+ */
+ binder_inc_node_tmpref_ilocked(node);
+ /* Need to drop inner lock to take node lock */
+ binder_inner_proc_unlock(proc);
+ if (last_node)
+ binder_put_node(last_node);
+ binder_node_inner_lock(node);
+ print_binder_node_nilocked(m, node);
+ binder_node_inner_unlock(node);
+ last_node = node;
+ binder_inner_proc_lock(proc);
+ }
+ binder_inner_proc_unlock(proc);
+ if (last_node)
+ binder_put_node(last_node);
+
if (print_all) {
+ binder_proc_lock(proc);
for (n = rb_first(&proc->refs_by_desc);
n != NULL;
n = rb_next(n))
- print_binder_ref(m, rb_entry(n, struct binder_ref,
- rb_node_desc));
+ print_binder_ref_olocked(m, rb_entry(n,
+ struct binder_ref,
+ rb_node_desc));
+ binder_proc_unlock(proc);
}
- for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n))
- print_binder_buffer(m, " buffer",
- rb_entry(n, struct binder_buffer, rb_node));
+ binder_alloc_print_allocated(m, &proc->alloc);
+ binder_inner_proc_lock(proc);
list_for_each_entry(w, &proc->todo, entry)
- print_binder_work(m, " ", " pending transaction", w);
+ print_binder_work_ilocked(m, proc, " ",
+ " pending transaction", w);
list_for_each_entry(w, &proc->delivered_death, entry) {
seq_puts(m, " has delivered dead binder\n");
break;
}
+ binder_inner_proc_unlock(proc);
if (!print_all && m->count == header_pos)
m->count = start_pos;
}
@@ -4084,54 +5333,45 @@ static const char * const binder_objstat_strings[] = {
"transaction_complete"
};
-static void add_binder_stats(struct binder_stats *from, struct binder_stats *to)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(to->bc); i++)
- to->bc[i] += from->bc[i];
-
- for (i = 0; i < ARRAY_SIZE(to->br); i++)
- to->br[i] += from->br[i];
-}
-
static void print_binder_stats(struct seq_file *m, const char *prefix,
- struct binder_stats *stats,
- struct binder_obj_stats *obj_stats)
+ struct binder_stats *stats)
{
int i;
BUILD_BUG_ON(ARRAY_SIZE(stats->bc) !=
ARRAY_SIZE(binder_command_strings));
for (i = 0; i < ARRAY_SIZE(stats->bc); i++) {
- if (stats->bc[i])
+ int temp = atomic_read(&stats->bc[i]);
+
+ if (temp)
seq_printf(m, "%s%s: %d\n", prefix,
- binder_command_strings[i], stats->bc[i]);
+ binder_command_strings[i], temp);
}
BUILD_BUG_ON(ARRAY_SIZE(stats->br) !=
ARRAY_SIZE(binder_return_strings));
for (i = 0; i < ARRAY_SIZE(stats->br); i++) {
- if (stats->br[i])
+ int temp = atomic_read(&stats->br[i]);
+
+ if (temp)
seq_printf(m, "%s%s: %d\n", prefix,
- binder_return_strings[i], stats->br[i]);
+ binder_return_strings[i], temp);
}
- if (!obj_stats)
- return;
-
- BUILD_BUG_ON(ARRAY_SIZE(obj_stats->obj_created) !=
+ BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) !=
ARRAY_SIZE(binder_objstat_strings));
- BUILD_BUG_ON(ARRAY_SIZE(obj_stats->obj_created) !=
- ARRAY_SIZE(obj_stats->obj_deleted));
- for (i = 0; i < ARRAY_SIZE(obj_stats->obj_created); i++) {
- int obj_created = atomic_read(&obj_stats->obj_created[i]);
- int obj_deleted = atomic_read(&obj_stats->obj_deleted[i]);
-
- if (obj_created || obj_deleted)
- seq_printf(m, "%s%s: active %d total %d\n", prefix,
- binder_objstat_strings[i],
- obj_created - obj_deleted, obj_created);
+ BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) !=
+ ARRAY_SIZE(stats->obj_deleted));
+ for (i = 0; i < ARRAY_SIZE(stats->obj_created); i++) {
+ int created = atomic_read(&stats->obj_created[i]);
+ int deleted = atomic_read(&stats->obj_deleted[i]);
+
+ if (created || deleted)
+ seq_printf(m, "%s%s: active %d total %d\n",
+ prefix,
+ binder_objstat_strings[i],
+ created - deleted,
+ created);
}
}
@@ -4139,226 +5379,193 @@ static void print_binder_proc_stats(struct seq_file *m,
struct binder_proc *proc)
{
struct binder_work *w;
+ struct binder_thread *thread;
struct rb_node *n;
- int count, strong, weak;
+ int count, strong, weak, ready_threads;
+ size_t free_async_space =
+ binder_alloc_get_free_async_space(&proc->alloc);
seq_printf(m, "proc %d\n", proc->pid);
seq_printf(m, "context %s\n", proc->context->name);
count = 0;
+ ready_threads = 0;
+ binder_inner_proc_lock(proc);
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
count++;
+
+ list_for_each_entry(thread, &proc->waiting_threads, waiting_thread_node)
+ ready_threads++;
+
seq_printf(m, " threads: %d\n", count);
seq_printf(m, " requested threads: %d+%d/%d\n"
" ready threads %d\n"
" free async space %zd\n", proc->requested_threads,
proc->requested_threads_started, proc->max_threads,
- proc->ready_threads, proc->free_async_space);
+ ready_threads,
+ free_async_space);
count = 0;
for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n))
count++;
+ binder_inner_proc_unlock(proc);
seq_printf(m, " nodes: %d\n", count);
count = 0;
strong = 0;
weak = 0;
+ binder_proc_lock(proc);
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
struct binder_ref *ref = rb_entry(n, struct binder_ref,
rb_node_desc);
count++;
- strong += ref->strong;
- weak += ref->weak;
+ strong += ref->data.strong;
+ weak += ref->data.weak;
}
+ binder_proc_unlock(proc);
seq_printf(m, " refs: %d s %d w %d\n", count, strong, weak);
- count = 0;
- for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n))
- count++;
+ count = binder_alloc_get_allocated_count(&proc->alloc);
seq_printf(m, " buffers: %d\n", count);
count = 0;
+ binder_inner_proc_lock(proc);
list_for_each_entry(w, &proc->todo, entry) {
- switch (w->type) {
- case BINDER_WORK_TRANSACTION:
+ if (w->type == BINDER_WORK_TRANSACTION)
count++;
- break;
- default:
- break;
- }
}
+ binder_inner_proc_unlock(proc);
seq_printf(m, " pending transactions: %d\n", count);
- print_binder_stats(m, " ", &proc->stats, NULL);
+ print_binder_stats(m, " ", &proc->stats);
}
static int binder_state_show(struct seq_file *m, void *unused)
{
- struct binder_device *device;
- struct binder_context *context;
struct binder_proc *proc;
struct binder_node *node;
- int do_lock = !binder_debug_no_lock;
- bool wrote_dead_nodes_header = false;
+ struct binder_node *last_node = NULL;
seq_puts(m, "binder state:\n");
- hlist_for_each_entry(device, &binder_devices, hlist) {
- context = &device->context;
- if (do_lock)
- binder_lock(context, __func__);
- if (!wrote_dead_nodes_header &&
- !hlist_empty(&context->binder_dead_nodes)) {
- seq_puts(m, "dead nodes:\n");
- wrote_dead_nodes_header = true;
- }
- hlist_for_each_entry(node, &context->binder_dead_nodes,
- dead_node)
- print_binder_node(m, node);
-
- if (do_lock)
- binder_unlock(context, __func__);
- }
-
- hlist_for_each_entry(device, &binder_devices, hlist) {
- context = &device->context;
- if (do_lock)
- binder_lock(context, __func__);
+ spin_lock(&binder_dead_nodes_lock);
+ if (!hlist_empty(&binder_dead_nodes))
+ seq_puts(m, "dead nodes:\n");
+ hlist_for_each_entry(node, &binder_dead_nodes, dead_node) {
+ /*
+ * take a temporary reference on the node so it
+ * survives and isn't removed from the list
+ * while we print it.
+ */
+ node->tmp_refs++;
+ spin_unlock(&binder_dead_nodes_lock);
+ if (last_node)
+ binder_put_node(last_node);
+ binder_node_lock(node);
+ print_binder_node_nilocked(m, node);
+ binder_node_unlock(node);
+ last_node = node;
+ spin_lock(&binder_dead_nodes_lock);
+ }
+ spin_unlock(&binder_dead_nodes_lock);
+ if (last_node)
+ binder_put_node(last_node);
+
+ mutex_lock(&binder_procs_lock);
+ hlist_for_each_entry(proc, &binder_procs, proc_node)
+ print_binder_proc(m, proc, 1);
+ mutex_unlock(&binder_procs_lock);
- hlist_for_each_entry(proc, &context->binder_procs, proc_node)
- print_binder_proc(m, proc, 1);
- if (do_lock)
- binder_unlock(context, __func__);
- }
return 0;
}
static int binder_stats_show(struct seq_file *m, void *unused)
{
- struct binder_device *device;
- struct binder_context *context;
struct binder_proc *proc;
- struct binder_stats total_binder_stats;
- int do_lock = !binder_debug_no_lock;
-
- memset(&total_binder_stats, 0, sizeof(struct binder_stats));
-
- hlist_for_each_entry(device, &binder_devices, hlist) {
- context = &device->context;
- if (do_lock)
- binder_lock(context, __func__);
-
- add_binder_stats(&context->binder_stats, &total_binder_stats);
-
- if (do_lock)
- binder_unlock(context, __func__);
- }
seq_puts(m, "binder stats:\n");
- print_binder_stats(m, "", &total_binder_stats, &binder_obj_stats);
- hlist_for_each_entry(device, &binder_devices, hlist) {
- context = &device->context;
- if (do_lock)
- binder_lock(context, __func__);
+ print_binder_stats(m, "", &binder_stats);
+
+ mutex_lock(&binder_procs_lock);
+ hlist_for_each_entry(proc, &binder_procs, proc_node)
+ print_binder_proc_stats(m, proc);
+ mutex_unlock(&binder_procs_lock);
- hlist_for_each_entry(proc, &context->binder_procs, proc_node)
- print_binder_proc_stats(m, proc);
- if (do_lock)
- binder_unlock(context, __func__);
- }
return 0;
}
static int binder_transactions_show(struct seq_file *m, void *unused)
{
- struct binder_device *device;
- struct binder_context *context;
struct binder_proc *proc;
- int do_lock = !binder_debug_no_lock;
seq_puts(m, "binder transactions:\n");
- hlist_for_each_entry(device, &binder_devices, hlist) {
- context = &device->context;
- if (do_lock)
- binder_lock(context, __func__);
-
- hlist_for_each_entry(proc, &context->binder_procs, proc_node)
- print_binder_proc(m, proc, 0);
- if (do_lock)
- binder_unlock(context, __func__);
- }
+ mutex_lock(&binder_procs_lock);
+ hlist_for_each_entry(proc, &binder_procs, proc_node)
+ print_binder_proc(m, proc, 0);
+ mutex_unlock(&binder_procs_lock);
+
return 0;
}
static int binder_proc_show(struct seq_file *m, void *unused)
{
- struct binder_device *device;
- struct binder_context *context;
struct binder_proc *itr;
int pid = (unsigned long)m->private;
- int do_lock = !binder_debug_no_lock;
- hlist_for_each_entry(device, &binder_devices, hlist) {
- context = &device->context;
- if (do_lock)
- binder_lock(context, __func__);
-
- hlist_for_each_entry(itr, &context->binder_procs, proc_node) {
- if (itr->pid == pid) {
- seq_puts(m, "binder proc state:\n");
- print_binder_proc(m, itr, 1);
- }
+ mutex_lock(&binder_procs_lock);
+ hlist_for_each_entry(itr, &binder_procs, proc_node) {
+ if (itr->pid == pid) {
+ seq_puts(m, "binder proc state:\n");
+ print_binder_proc(m, itr, 1);
}
- if (do_lock)
- binder_unlock(context, __func__);
}
+ mutex_unlock(&binder_procs_lock);
+
return 0;
}
static void print_binder_transaction_log_entry(struct seq_file *m,
struct binder_transaction_log_entry *e)
{
+ int debug_id = READ_ONCE(e->debug_id_done);
+ /*
+ * read barrier to guarantee debug_id_done read before
+ * we print the log values
+ */
+ smp_rmb();
seq_printf(m,
- "%d: %s from %d:%d to %d:%d context %s node %d handle %d size %d:%d\n",
+ "%d: %s from %d:%d to %d:%d context %s node %d handle %d size %d:%d ret %d/%d l=%d",
e->debug_id, (e->call_type == 2) ? "reply" :
((e->call_type == 1) ? "async" : "call "), e->from_proc,
e->from_thread, e->to_proc, e->to_thread, e->context_name,
- e->to_node, e->target_handle, e->data_size, e->offsets_size);
-}
-
-static int print_binder_transaction_log(struct seq_file *m,
- struct binder_transaction_log *log)
-{
- int i;
- if (log->full) {
- for (i = log->next; i < ARRAY_SIZE(log->entry); i++)
- print_binder_transaction_log_entry(m, &log->entry[i]);
- }
- for (i = 0; i < log->next; i++)
- print_binder_transaction_log_entry(m, &log->entry[i]);
- return 0;
+ e->to_node, e->target_handle, e->data_size, e->offsets_size,
+ e->return_error, e->return_error_param,
+ e->return_error_line);
+ /*
+ * read-barrier to guarantee read of debug_id_done after
+ * done printing the fields of the entry
+ */
+ smp_rmb();
+ seq_printf(m, debug_id && debug_id == READ_ONCE(e->debug_id_done) ?
+ "\n" : " (incomplete)\n");
}
static int binder_transaction_log_show(struct seq_file *m, void *unused)
{
- struct binder_device *device;
- struct binder_context *context;
-
- hlist_for_each_entry(device, &binder_devices, hlist) {
- context = &device->context;
- print_binder_transaction_log(m, &context->transaction_log);
- }
- return 0;
-}
+ struct binder_transaction_log *log = m->private;
+ unsigned int log_cur = atomic_read(&log->cur);
+ unsigned int count;
+ unsigned int cur;
+ int i;
-static int binder_failed_transaction_log_show(struct seq_file *m, void *unused)
-{
- struct binder_device *device;
- struct binder_context *context;
+ count = log_cur + 1;
+ cur = count < ARRAY_SIZE(log->entry) && !log->full ?
+ 0 : count % ARRAY_SIZE(log->entry);
+ if (count > ARRAY_SIZE(log->entry) || log->full)
+ count = ARRAY_SIZE(log->entry);
+ for (i = 0; i < count; i++) {
+ unsigned int index = cur++ % ARRAY_SIZE(log->entry);
- hlist_for_each_entry(device, &binder_devices, hlist) {
- context = &device->context;
- print_binder_transaction_log(m,
- &context->transaction_log_failed);
+ print_binder_transaction_log_entry(m, &log->entry[index]);
}
return 0;
}
@@ -4378,20 +5585,11 @@ BINDER_DEBUG_ENTRY(state);
BINDER_DEBUG_ENTRY(stats);
BINDER_DEBUG_ENTRY(transactions);
BINDER_DEBUG_ENTRY(transaction_log);
-BINDER_DEBUG_ENTRY(failed_transaction_log);
-
-static void __init free_binder_device(struct binder_device *device)
-{
- if (device->context.binder_deferred_workqueue)
- destroy_workqueue(device->context.binder_deferred_workqueue);
- kfree(device);
-}
static int __init init_binder_device(const char *name)
{
int ret;
struct binder_device *binder_device;
- struct binder_context *context;
binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
if (!binder_device)
@@ -4401,65 +5599,34 @@ static int __init init_binder_device(const char *name)
binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
binder_device->miscdev.name = name;
- context = &binder_device->context;
- context->binder_context_mgr_uid = INVALID_UID;
- context->name = name;
-
- mutex_init(&context->binder_main_lock);
- mutex_init(&context->binder_deferred_lock);
- mutex_init(&context->binder_mmap_lock);
-
- context->binder_deferred_workqueue =
- create_singlethread_workqueue(name);
-
- if (!context->binder_deferred_workqueue) {
- ret = -ENOMEM;
- goto err_create_singlethread_workqueue_failed;
- }
-
- INIT_HLIST_HEAD(&context->binder_procs);
- INIT_HLIST_HEAD(&context->binder_dead_nodes);
- INIT_HLIST_HEAD(&context->binder_deferred_list);
- INIT_WORK(&context->deferred_work, binder_deferred_func);
+ binder_device->context.binder_context_mgr_uid = INVALID_UID;
+ binder_device->context.name = name;
+ mutex_init(&binder_device->context.context_mgr_node_lock);
ret = misc_register(&binder_device->miscdev);
if (ret < 0) {
- goto err_misc_register_failed;
+ kfree(binder_device);
+ return ret;
}
hlist_add_head(&binder_device->hlist, &binder_devices);
- return ret;
-
-err_create_singlethread_workqueue_failed:
-err_misc_register_failed:
- free_binder_device(binder_device);
return ret;
}
static int __init binder_init(void)
{
- int ret = 0;
+ int ret;
char *device_name, *device_names;
struct binder_device *device;
struct hlist_node *tmp;
- /*
- * Copy the module_parameter string, because we don't want to
- * tokenize it in-place.
- */
- device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
- if (!device_names)
+ atomic_set(&binder_transaction_log.cur, ~0U);
+ atomic_set(&binder_transaction_log_failed.cur, ~0U);
+ binder_deferred_workqueue = create_singlethread_workqueue("binder");
+ if (!binder_deferred_workqueue)
return -ENOMEM;
- strcpy(device_names, binder_devices_param);
-
- while ((device_name = strsep(&device_names, ","))) {
- ret = init_binder_device(device_name);
- if (ret)
- goto err_init_binder_device_failed;
- }
-
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root)
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
@@ -4484,13 +5651,30 @@ static int __init binder_init(void)
debugfs_create_file("transaction_log",
S_IRUGO,
binder_debugfs_dir_entry_root,
- NULL,
+ &binder_transaction_log,
&binder_transaction_log_fops);
debugfs_create_file("failed_transaction_log",
S_IRUGO,
binder_debugfs_dir_entry_root,
- NULL,
- &binder_failed_transaction_log_fops);
+ &binder_transaction_log_failed,
+ &binder_transaction_log_fops);
+ }
+
+ /*
+ * Copy the module_parameter string, because we don't want to
+ * tokenize it in-place.
+ */
+ device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
+ if (!device_names) {
+ ret = -ENOMEM;
+ goto err_alloc_device_names_failed;
+ }
+ strcpy(device_names, binder_devices_param);
+
+ while ((device_name = strsep(&device_names, ","))) {
+ ret = init_binder_device(device_name);
+ if (ret)
+ goto err_init_binder_device_failed;
}
return ret;
@@ -4499,8 +5683,12 @@ err_init_binder_device_failed:
hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
misc_deregister(&device->miscdev);
hlist_del(&device->hlist);
- free_binder_device(device);
+ kfree(device);
}
+err_alloc_device_names_failed:
+ debugfs_remove_recursive(binder_debugfs_dir_entry_root);
+
+ destroy_workqueue(binder_deferred_workqueue);
return ret;
}
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
new file mode 100644
index 000000000000..aabfebac6e57
--- /dev/null
+++ b/drivers/android/binder_alloc.c
@@ -0,0 +1,802 @@
+/* binder_alloc.c
+ *
+ * Android IPC Subsystem
+ *
+ * Copyright (C) 2007-2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <asm/cacheflush.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/rtmutex.h>
+#include <linux/rbtree.h>
+#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include "binder_alloc.h"
+#include "binder_trace.h"
+
+static DEFINE_MUTEX(binder_alloc_mmap_lock);
+
+enum {
+ BINDER_DEBUG_OPEN_CLOSE = 1U << 1,
+ BINDER_DEBUG_BUFFER_ALLOC = 1U << 2,
+ BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 3,
+};
+static uint32_t binder_alloc_debug_mask;
+
+module_param_named(debug_mask, binder_alloc_debug_mask,
+ uint, S_IWUSR | S_IRUGO);
+
+#define binder_alloc_debug(mask, x...) \
+ do { \
+ if (binder_alloc_debug_mask & mask) \
+ pr_info(x); \
+ } while (0)
+
+static size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
+ struct binder_buffer *buffer)
+{
+ if (list_is_last(&buffer->entry, &alloc->buffers))
+ return alloc->buffer +
+ alloc->buffer_size - (void *)buffer->data;
+ return (size_t)list_entry(buffer->entry.next,
+ struct binder_buffer, entry) - (size_t)buffer->data;
+}
+
+static void binder_insert_free_buffer(struct binder_alloc *alloc,
+ struct binder_buffer *new_buffer)
+{
+ struct rb_node **p = &alloc->free_buffers.rb_node;
+ struct rb_node *parent = NULL;
+ struct binder_buffer *buffer;
+ size_t buffer_size;
+ size_t new_buffer_size;
+
+ BUG_ON(!new_buffer->free);
+
+ new_buffer_size = binder_alloc_buffer_size(alloc, new_buffer);
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: add free buffer, size %zd, at %pK\n",
+ alloc->pid, new_buffer_size, new_buffer);
+
+ while (*p) {
+ parent = *p;
+ buffer = rb_entry(parent, struct binder_buffer, rb_node);
+ BUG_ON(!buffer->free);
+
+ buffer_size = binder_alloc_buffer_size(alloc, buffer);
+
+ if (new_buffer_size < buffer_size)
+ p = &parent->rb_left;
+ else
+ p = &parent->rb_right;
+ }
+ rb_link_node(&new_buffer->rb_node, parent, p);
+ rb_insert_color(&new_buffer->rb_node, &alloc->free_buffers);
+}
+
+static void binder_insert_allocated_buffer_locked(
+ struct binder_alloc *alloc, struct binder_buffer *new_buffer)
+{
+ struct rb_node **p = &alloc->allocated_buffers.rb_node;
+ struct rb_node *parent = NULL;
+ struct binder_buffer *buffer;
+
+ BUG_ON(new_buffer->free);
+
+ while (*p) {
+ parent = *p;
+ buffer = rb_entry(parent, struct binder_buffer, rb_node);
+ BUG_ON(buffer->free);
+
+ if (new_buffer < buffer)
+ p = &parent->rb_left;
+ else if (new_buffer > buffer)
+ p = &parent->rb_right;
+ else
+ BUG();
+ }
+ rb_link_node(&new_buffer->rb_node, parent, p);
+ rb_insert_color(&new_buffer->rb_node, &alloc->allocated_buffers);
+}
+
+static struct binder_buffer *binder_alloc_prepare_to_free_locked(
+ struct binder_alloc *alloc,
+ uintptr_t user_ptr)
+{
+ struct rb_node *n = alloc->allocated_buffers.rb_node;
+ struct binder_buffer *buffer;
+ struct binder_buffer *kern_ptr;
+
+ kern_ptr = (struct binder_buffer *)(user_ptr - alloc->user_buffer_offset
+ - offsetof(struct binder_buffer, data));
+
+ while (n) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ BUG_ON(buffer->free);
+
+ if (kern_ptr < buffer)
+ n = n->rb_left;
+ else if (kern_ptr > buffer)
+ n = n->rb_right;
+ else {
+ /*
+ * Guard against user threads attempting to
+ * free the buffer twice
+ */
+ if (buffer->free_in_progress) {
+ pr_err("%d:%d FREE_BUFFER u%016llx user freed buffer twice\n",
+ alloc->pid, current->pid, (u64)user_ptr);
+ return NULL;
+ }
+ buffer->free_in_progress = 1;
+ return buffer;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * binder_alloc_buffer_lookup() - get buffer given user ptr
+ * @alloc: binder_alloc for this proc
+ * @user_ptr: User pointer to buffer data
+ *
+ * Validate userspace pointer to buffer data and return buffer corresponding to
+ * that user pointer. Search the rb tree for buffer that matches user data
+ * pointer.
+ *
+ * Return: Pointer to buffer or NULL
+ */
+struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc,
+ uintptr_t user_ptr)
+{
+ struct binder_buffer *buffer;
+
+ mutex_lock(&alloc->mutex);
+ buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr);
+ mutex_unlock(&alloc->mutex);
+ return buffer;
+}
+
+static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
+ void *start, void *end,
+ struct vm_area_struct *vma)
+{
+ void *page_addr;
+ unsigned long user_page_addr;
+ struct page **page;
+ struct mm_struct *mm;
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: %s pages %pK-%pK\n", alloc->pid,
+ allocate ? "allocate" : "free", start, end);
+
+ if (end <= start)
+ return 0;
+
+ trace_binder_update_page_range(alloc, allocate, start, end);
+
+ if (vma)
+ mm = NULL;
+ else
+ mm = get_task_mm(alloc->tsk);
+
+ if (mm) {
+ down_write(&mm->mmap_sem);
+ vma = alloc->vma;
+ if (vma && mm != alloc->vma_vm_mm) {
+ pr_err("%d: vma mm and task mm mismatch\n",
+ alloc->pid);
+ vma = NULL;
+ }
+ }
+
+ if (allocate == 0)
+ goto free_range;
+
+ if (vma == NULL) {
+ pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
+ alloc->pid);
+ goto err_no_vma;
+ }
+
+ for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
+ int ret;
+
+ page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
+
+ BUG_ON(*page);
+ *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
+ if (*page == NULL) {
+ pr_err("%d: binder_alloc_buf failed for page at %pK\n",
+ alloc->pid, page_addr);
+ goto err_alloc_page_failed;
+ }
+ ret = map_kernel_range_noflush((unsigned long)page_addr,
+ PAGE_SIZE, PAGE_KERNEL, page);
+ flush_cache_vmap((unsigned long)page_addr,
+ (unsigned long)page_addr + PAGE_SIZE);
+ if (ret != 1) {
+ pr_err("%d: binder_alloc_buf failed to map page at %pK in kernel\n",
+ alloc->pid, page_addr);
+ goto err_map_kernel_failed;
+ }
+ user_page_addr =
+ (uintptr_t)page_addr + alloc->user_buffer_offset;
+ ret = vm_insert_page(vma, user_page_addr, page[0]);
+ if (ret) {
+ pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
+ alloc->pid, user_page_addr);
+ goto err_vm_insert_page_failed;
+ }
+ /* vm_insert_page does not seem to increment the refcount */
+ }
+ if (mm) {
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ }
+ return 0;
+
+free_range:
+ for (page_addr = end - PAGE_SIZE; page_addr >= start;
+ page_addr -= PAGE_SIZE) {
+ page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
+ if (vma)
+ zap_page_range(vma, (uintptr_t)page_addr +
+ alloc->user_buffer_offset, PAGE_SIZE, NULL);
+err_vm_insert_page_failed:
+ unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
+err_map_kernel_failed:
+ __free_page(*page);
+ *page = NULL;
+err_alloc_page_failed:
+ ;
+ }
+err_no_vma:
+ if (mm) {
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ }
+ return vma ? -ENOMEM : -ESRCH;
+}
+
+struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc,
+ size_t data_size,
+ size_t offsets_size,
+ size_t extra_buffers_size,
+ int is_async)
+{
+ struct rb_node *n = alloc->free_buffers.rb_node;
+ struct binder_buffer *buffer;
+ size_t buffer_size;
+ struct rb_node *best_fit = NULL;
+ void *has_page_addr;
+ void *end_page_addr;
+ size_t size, data_offsets_size;
+ int ret;
+
+ if (alloc->vma == NULL) {
+ pr_err("%d: binder_alloc_buf, no vma\n",
+ alloc->pid);
+ return ERR_PTR(-ESRCH);
+ }
+
+ data_offsets_size = ALIGN(data_size, sizeof(void *)) +
+ ALIGN(offsets_size, sizeof(void *));
+
+ if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: got transaction with invalid size %zd-%zd\n",
+ alloc->pid, data_size, offsets_size);
+ return ERR_PTR(-EINVAL);
+ }
+ size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
+ if (size < data_offsets_size || size < extra_buffers_size) {
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: got transaction with invalid extra_buffers_size %zd\n",
+ alloc->pid, extra_buffers_size);
+ return ERR_PTR(-EINVAL);
+ }
+ if (is_async &&
+ alloc->free_async_space < size + sizeof(struct binder_buffer)) {
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: binder_alloc_buf size %zd failed, no async space left\n",
+ alloc->pid, size);
+ return ERR_PTR(-ENOSPC);
+ }
+
+ while (n) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ BUG_ON(!buffer->free);
+ buffer_size = binder_alloc_buffer_size(alloc, buffer);
+
+ if (size < buffer_size) {
+ best_fit = n;
+ n = n->rb_left;
+ } else if (size > buffer_size)
+ n = n->rb_right;
+ else {
+ best_fit = n;
+ break;
+ }
+ }
+ if (best_fit == NULL) {
+ size_t allocated_buffers = 0;
+ size_t largest_alloc_size = 0;
+ size_t total_alloc_size = 0;
+ size_t free_buffers = 0;
+ size_t largest_free_size = 0;
+ size_t total_free_size = 0;
+
+ for (n = rb_first(&alloc->allocated_buffers); n != NULL;
+ n = rb_next(n)) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ buffer_size = binder_alloc_buffer_size(alloc, buffer);
+ allocated_buffers++;
+ total_alloc_size += buffer_size;
+ if (buffer_size > largest_alloc_size)
+ largest_alloc_size = buffer_size;
+ }
+ for (n = rb_first(&alloc->free_buffers); n != NULL;
+ n = rb_next(n)) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ buffer_size = binder_alloc_buffer_size(alloc, buffer);
+ free_buffers++;
+ total_free_size += buffer_size;
+ if (buffer_size > largest_free_size)
+ largest_free_size = buffer_size;
+ }
+ pr_err("%d: binder_alloc_buf size %zd failed, no address space\n",
+ alloc->pid, size);
+ pr_err("allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n",
+ total_alloc_size, allocated_buffers, largest_alloc_size,
+ total_free_size, free_buffers, largest_free_size);
+ return ERR_PTR(-ENOSPC);
+ }
+ if (n == NULL) {
+ buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
+ buffer_size = binder_alloc_buffer_size(alloc, buffer);
+ }
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n",
+ alloc->pid, size, buffer, buffer_size);
+
+ has_page_addr =
+ (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK);
+ if (n == NULL) {
+ if (size + sizeof(struct binder_buffer) + 4 >= buffer_size)
+ buffer_size = size; /* no room for other buffers */
+ else
+ buffer_size = size + sizeof(struct binder_buffer);
+ }
+ end_page_addr =
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
+ if (end_page_addr > has_page_addr)
+ end_page_addr = has_page_addr;
+ ret = binder_update_page_range(alloc, 1,
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL);
+ if (ret)
+ return ERR_PTR(ret);
+
+ rb_erase(best_fit, &alloc->free_buffers);
+ buffer->free = 0;
+ buffer->free_in_progress = 0;
+ binder_insert_allocated_buffer_locked(alloc, buffer);
+ if (buffer_size != size) {
+ struct binder_buffer *new_buffer = (void *)buffer->data + size;
+
+ list_add(&new_buffer->entry, &buffer->entry);
+ new_buffer->free = 1;
+ binder_insert_free_buffer(alloc, new_buffer);
+ }
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: binder_alloc_buf size %zd got %pK\n",
+ alloc->pid, size, buffer);
+ buffer->data_size = data_size;
+ buffer->offsets_size = offsets_size;
+ buffer->async_transaction = is_async;
+ buffer->extra_buffers_size = extra_buffers_size;
+ if (is_async) {
+ alloc->free_async_space -= size + sizeof(struct binder_buffer);
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
+ "%d: binder_alloc_buf size %zd async free %zd\n",
+ alloc->pid, size, alloc->free_async_space);
+ }
+ return buffer;
+}
+
+/**
+ * binder_alloc_new_buf() - Allocate a new binder buffer
+ * @alloc: binder_alloc for this proc
+ * @data_size: size of user data buffer
+ * @offsets_size: user specified buffer offset
+ * @extra_buffers_size: size of extra space for meta-data (eg, security context)
+ * @is_async: buffer for async transaction
+ *
+ * Allocate a new buffer given the requested sizes. Returns
+ * the kernel version of the buffer pointer. The size allocated
+ * is the sum of the three given sizes (each rounded up to
+ * pointer-sized boundary)
+ *
+ * Return: The allocated buffer or %NULL if error
+ */
+struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
+ size_t data_size,
+ size_t offsets_size,
+ size_t extra_buffers_size,
+ int is_async)
+{
+ struct binder_buffer *buffer;
+
+ mutex_lock(&alloc->mutex);
+ buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size,
+ extra_buffers_size, is_async);
+ mutex_unlock(&alloc->mutex);
+ return buffer;
+}
+
+static void *buffer_start_page(struct binder_buffer *buffer)
+{
+ return (void *)((uintptr_t)buffer & PAGE_MASK);
+}
+
+static void *buffer_end_page(struct binder_buffer *buffer)
+{
+ return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK);
+}
+
+static void binder_delete_free_buffer(struct binder_alloc *alloc,
+ struct binder_buffer *buffer)
+{
+ struct binder_buffer *prev, *next = NULL;
+ int free_page_end = 1;
+ int free_page_start = 1;
+
+ BUG_ON(alloc->buffers.next == &buffer->entry);
+ prev = list_entry(buffer->entry.prev, struct binder_buffer, entry);
+ BUG_ON(!prev->free);
+ if (buffer_end_page(prev) == buffer_start_page(buffer)) {
+ free_page_start = 0;
+ if (buffer_end_page(prev) == buffer_end_page(buffer))
+ free_page_end = 0;
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: merge free, buffer %pK share page with %pK\n",
+ alloc->pid, buffer, prev);
+ }
+
+ if (!list_is_last(&buffer->entry, &alloc->buffers)) {
+ next = list_entry(buffer->entry.next,
+ struct binder_buffer, entry);
+ if (buffer_start_page(next) == buffer_end_page(buffer)) {
+ free_page_end = 0;
+ if (buffer_start_page(next) ==
+ buffer_start_page(buffer))
+ free_page_start = 0;
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: merge free, buffer %pK share page with %pK\n",
+ alloc->pid, buffer, prev);
+ }
+ }
+ list_del(&buffer->entry);
+ if (free_page_start || free_page_end) {
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: merge free, buffer %pK do not share page%s%s with %pK or %pK\n",
+ alloc->pid, buffer, free_page_start ? "" : " end",
+ free_page_end ? "" : " start", prev, next);
+ binder_update_page_range(alloc, 0, free_page_start ?
+ buffer_start_page(buffer) : buffer_end_page(buffer),
+ (free_page_end ? buffer_end_page(buffer) :
+ buffer_start_page(buffer)) + PAGE_SIZE, NULL);
+ }
+}
+
+static void binder_free_buf_locked(struct binder_alloc *alloc,
+ struct binder_buffer *buffer)
+{
+ size_t size, buffer_size;
+
+ buffer_size = binder_alloc_buffer_size(alloc, buffer);
+
+ size = ALIGN(buffer->data_size, sizeof(void *)) +
+ ALIGN(buffer->offsets_size, sizeof(void *)) +
+ ALIGN(buffer->extra_buffers_size, sizeof(void *));
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: binder_free_buf %pK size %zd buffer_size %zd\n",
+ alloc->pid, buffer, size, buffer_size);
+
+ BUG_ON(buffer->free);
+ BUG_ON(size > buffer_size);
+ BUG_ON(buffer->transaction != NULL);
+ BUG_ON((void *)buffer < alloc->buffer);
+ BUG_ON((void *)buffer > alloc->buffer + alloc->buffer_size);
+
+ if (buffer->async_transaction) {
+ alloc->free_async_space += size + sizeof(struct binder_buffer);
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
+ "%d: binder_free_buf size %zd async free %zd\n",
+ alloc->pid, size, alloc->free_async_space);
+ }
+
+ binder_update_page_range(alloc, 0,
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data),
+ (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
+ NULL);
+
+ rb_erase(&buffer->rb_node, &alloc->allocated_buffers);
+ buffer->free = 1;
+ if (!list_is_last(&buffer->entry, &alloc->buffers)) {
+ struct binder_buffer *next = list_entry(buffer->entry.next,
+ struct binder_buffer, entry);
+
+ if (next->free) {
+ rb_erase(&next->rb_node, &alloc->free_buffers);
+ binder_delete_free_buffer(alloc, next);
+ }
+ }
+ if (alloc->buffers.next != &buffer->entry) {
+ struct binder_buffer *prev = list_entry(buffer->entry.prev,
+ struct binder_buffer, entry);
+
+ if (prev->free) {
+ binder_delete_free_buffer(alloc, buffer);
+ rb_erase(&prev->rb_node, &alloc->free_buffers);
+ buffer = prev;
+ }
+ }
+ binder_insert_free_buffer(alloc, buffer);
+}
+
+/**
+ * binder_alloc_free_buf() - free a binder buffer
+ * @alloc: binder_alloc for this proc
+ * @buffer: kernel pointer to buffer
+ *
+ * Free the buffer allocated via binder_alloc_new_buffer()
+ */
+void binder_alloc_free_buf(struct binder_alloc *alloc,
+ struct binder_buffer *buffer)
+{
+ mutex_lock(&alloc->mutex);
+ binder_free_buf_locked(alloc, buffer);
+ mutex_unlock(&alloc->mutex);
+}
+
+/**
+ * binder_alloc_mmap_handler() - map virtual address space for proc
+ * @alloc: alloc structure for this proc
+ * @vma: vma passed to mmap()
+ *
+ * Called by binder_mmap() to initialize the space specified in
+ * vma for allocating binder buffers
+ *
+ * Return:
+ * 0 = success
+ * -EBUSY = address space already mapped
+ * -ENOMEM = failed to map memory to given address space
+ */
+int binder_alloc_mmap_handler(struct binder_alloc *alloc,
+ struct vm_area_struct *vma)
+{
+ int ret;
+ struct vm_struct *area;
+ const char *failure_string;
+ struct binder_buffer *buffer;
+
+ mutex_lock(&binder_alloc_mmap_lock);
+ if (alloc->buffer) {
+ ret = -EBUSY;
+ failure_string = "already mapped";
+ goto err_already_mapped;
+ }
+
+ area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
+ if (area == NULL) {
+ ret = -ENOMEM;
+ failure_string = "get_vm_area";
+ goto err_get_vm_area_failed;
+ }
+ alloc->buffer = area->addr;
+ alloc->user_buffer_offset =
+ vma->vm_start - (uintptr_t)alloc->buffer;
+ mutex_unlock(&binder_alloc_mmap_lock);
+
+#ifdef CONFIG_CPU_CACHE_VIPT
+ if (cache_is_vipt_aliasing()) {
+ while (CACHE_COLOUR(
+ (vma->vm_start ^ (uint32_t)alloc->buffer))) {
+ pr_info("binder_mmap: %d %lx-%lx maps %pK bad alignment\n",
+ alloc->pid, vma->vm_start, vma->vm_end,
+ alloc->buffer);
+ vma->vm_start += PAGE_SIZE;
+ }
+ }
+#endif
+ alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
+ ((vma->vm_end - vma->vm_start) / PAGE_SIZE),
+ GFP_KERNEL);
+ if (alloc->pages == NULL) {
+ ret = -ENOMEM;
+ failure_string = "alloc page array";
+ goto err_alloc_pages_failed;
+ }
+ alloc->buffer_size = vma->vm_end - vma->vm_start;
+
+ if (binder_update_page_range(alloc, 1, alloc->buffer,
+ alloc->buffer + PAGE_SIZE, vma)) {
+ ret = -ENOMEM;
+ failure_string = "alloc small buf";
+ goto err_alloc_small_buf_failed;
+ }
+ buffer = alloc->buffer;
+ INIT_LIST_HEAD(&alloc->buffers);
+ list_add(&buffer->entry, &alloc->buffers);
+ buffer->free = 1;
+ binder_insert_free_buffer(alloc, buffer);
+ alloc->free_async_space = alloc->buffer_size / 2;
+ barrier();
+ alloc->vma = vma;
+ alloc->vma_vm_mm = vma->vm_mm;
+
+ return 0;
+
+err_alloc_small_buf_failed:
+ kfree(alloc->pages);
+ alloc->pages = NULL;
+err_alloc_pages_failed:
+ mutex_lock(&binder_alloc_mmap_lock);
+ vfree(alloc->buffer);
+ alloc->buffer = NULL;
+err_get_vm_area_failed:
+err_already_mapped:
+ mutex_unlock(&binder_alloc_mmap_lock);
+ pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
+ alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
+ return ret;
+}
+
+
+void binder_alloc_deferred_release(struct binder_alloc *alloc)
+{
+ struct rb_node *n;
+ int buffers, page_count;
+
+ BUG_ON(alloc->vma);
+
+ buffers = 0;
+ mutex_lock(&alloc->mutex);
+ while ((n = rb_first(&alloc->allocated_buffers))) {
+ struct binder_buffer *buffer;
+
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+
+ /* Transaction should already have been freed */
+ BUG_ON(buffer->transaction);
+
+ binder_free_buf_locked(alloc, buffer);
+ buffers++;
+ }
+
+ page_count = 0;
+ if (alloc->pages) {
+ int i;
+
+ for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
+ void *page_addr;
+
+ if (!alloc->pages[i])
+ continue;
+
+ page_addr = alloc->buffer + i * PAGE_SIZE;
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%s: %d: page %d at %pK not freed\n",
+ __func__, alloc->pid, i, page_addr);
+ unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
+ __free_page(alloc->pages[i]);
+ page_count++;
+ }
+ kfree(alloc->pages);
+ vfree(alloc->buffer);
+ }
+ mutex_unlock(&alloc->mutex);
+
+ binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE,
+ "%s: %d buffers %d, pages %d\n",
+ __func__, alloc->pid, buffers, page_count);
+}
+
+static void print_binder_buffer(struct seq_file *m, const char *prefix,
+ struct binder_buffer *buffer)
+{
+ seq_printf(m, "%s %d: %pK size %zd:%zd:%zd %s\n",
+ prefix, buffer->debug_id, buffer->data,
+ buffer->data_size, buffer->offsets_size,
+ buffer->extra_buffers_size,
+ buffer->transaction ? "active" : "delivered");
+}
+
+/**
+ * binder_alloc_print_allocated() - print buffer info
+ * @m: seq_file for output via seq_printf()
+ * @alloc: binder_alloc for this proc
+ *
+ * Prints information about every buffer associated with
+ * the binder_alloc state to the given seq_file
+ */
+void binder_alloc_print_allocated(struct seq_file *m,
+ struct binder_alloc *alloc)
+{
+ struct rb_node *n;
+
+ mutex_lock(&alloc->mutex);
+ for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
+ print_binder_buffer(m, " buffer",
+ rb_entry(n, struct binder_buffer, rb_node));
+ mutex_unlock(&alloc->mutex);
+}
+
+/**
+ * binder_alloc_get_allocated_count() - return count of buffers
+ * @alloc: binder_alloc for this proc
+ *
+ * Return: count of allocated buffers
+ */
+int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
+{
+ struct rb_node *n;
+ int count = 0;
+
+ mutex_lock(&alloc->mutex);
+ for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
+ count++;
+ mutex_unlock(&alloc->mutex);
+ return count;
+}
+
+
+/**
+ * binder_alloc_vma_close() - invalidate address space
+ * @alloc: binder_alloc for this proc
+ *
+ * Called from binder_vma_close() when releasing address space.
+ * Clears alloc->vma to prevent new incoming transactions from
+ * allocating more buffers.
+ */
+void binder_alloc_vma_close(struct binder_alloc *alloc)
+{
+ WRITE_ONCE(alloc->vma, NULL);
+ WRITE_ONCE(alloc->vma_vm_mm, NULL);
+}
+
+/**
+ * binder_alloc_init() - called by binder_open() for per-proc initialization
+ * @alloc: binder_alloc for this proc
+ *
+ * Called from binder_open() to initialize binder_alloc fields for
+ * new binder proc
+ */
+void binder_alloc_init(struct binder_alloc *alloc)
+{
+ alloc->tsk = current->group_leader;
+ alloc->pid = current->group_leader->pid;
+ mutex_init(&alloc->mutex);
+}
+
diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h
new file mode 100644
index 000000000000..088e4ffc6230
--- /dev/null
+++ b/drivers/android/binder_alloc.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_BINDER_ALLOC_H
+#define _LINUX_BINDER_ALLOC_H
+
+#include <linux/rbtree.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/rtmutex.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+struct binder_transaction;
+
+/**
+ * struct binder_buffer - buffer used for binder transactions
+ * @entry: entry alloc->buffers
+ * @rb_node: node for allocated_buffers/free_buffers rb trees
+ * @free: true if buffer is free
+ * @allow_user_free: describe the second member of struct blah,
+ * @async_transaction: describe the second member of struct blah,
+ * @debug_id: describe the second member of struct blah,
+ * @transaction: describe the second member of struct blah,
+ * @target_node: describe the second member of struct blah,
+ * @data_size: describe the second member of struct blah,
+ * @offsets_size: describe the second member of struct blah,
+ * @extra_buffers_size: describe the second member of struct blah,
+ * @data:i describe the second member of struct blah,
+ *
+ * Bookkeeping structure for binder transaction buffers
+ */
+struct binder_buffer {
+ struct list_head entry; /* free and allocated entries by address */
+ struct rb_node rb_node; /* free entry by size or allocated entry */
+ /* by address */
+ unsigned free:1;
+ unsigned allow_user_free:1;
+ unsigned async_transaction:1;
+ unsigned free_in_progress:1;
+ unsigned debug_id:28;
+
+ struct binder_transaction *transaction;
+
+ struct binder_node *target_node;
+ size_t data_size;
+ size_t offsets_size;
+ size_t extra_buffers_size;
+ uint8_t data[0];
+};
+
+/**
+ * struct binder_alloc - per-binder proc state for binder allocator
+ * @vma: vm_area_struct passed to mmap_handler
+ * (invarient after mmap)
+ * @tsk: tid for task that called init for this proc
+ * (invariant after init)
+ * @vma_vm_mm: copy of vma->vm_mm (invarient after mmap)
+ * @buffer: base of per-proc address space mapped via mmap
+ * @user_buffer_offset: offset between user and kernel VAs for buffer
+ * @buffers: list of all buffers for this proc
+ * @free_buffers: rb tree of buffers available for allocation
+ * sorted by size
+ * @allocated_buffers: rb tree of allocated buffers sorted by address
+ * @free_async_space: VA space available for async buffers. This is
+ * initialized at mmap time to 1/2 the full VA space
+ * @pages: array of physical page addresses for each
+ * page of mmap'd space
+ * @buffer_size: size of address space specified via mmap
+ * @pid: pid for associated binder_proc (invariant after init)
+ *
+ * Bookkeeping structure for per-proc address space management for binder
+ * buffers. It is normally initialized during binder_init() and binder_mmap()
+ * calls. The address space is used for both user-visible buffers and for
+ * struct binder_buffer objects used to track the user buffers
+ */
+struct binder_alloc {
+ struct mutex mutex;
+ struct task_struct *tsk;
+ struct vm_area_struct *vma;
+ struct mm_struct *vma_vm_mm;
+ void *buffer;
+ ptrdiff_t user_buffer_offset;
+ struct list_head buffers;
+ struct rb_root free_buffers;
+ struct rb_root allocated_buffers;
+ size_t free_async_space;
+ struct page **pages;
+ size_t buffer_size;
+ uint32_t buffer_free;
+ int pid;
+};
+
+extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
+ size_t data_size,
+ size_t offsets_size,
+ size_t extra_buffers_size,
+ int is_async);
+extern void binder_alloc_init(struct binder_alloc *alloc);
+extern void binder_alloc_vma_close(struct binder_alloc *alloc);
+extern struct binder_buffer *
+binder_alloc_prepare_to_free(struct binder_alloc *alloc,
+ uintptr_t user_ptr);
+extern void binder_alloc_free_buf(struct binder_alloc *alloc,
+ struct binder_buffer *buffer);
+extern int binder_alloc_mmap_handler(struct binder_alloc *alloc,
+ struct vm_area_struct *vma);
+extern void binder_alloc_deferred_release(struct binder_alloc *alloc);
+extern int binder_alloc_get_allocated_count(struct binder_alloc *alloc);
+extern void binder_alloc_print_allocated(struct seq_file *m,
+ struct binder_alloc *alloc);
+
+/**
+ * binder_alloc_get_free_async_space() - get free space available for async
+ * @alloc: binder_alloc for this proc
+ *
+ * Return: the bytes remaining in the address-space for async transactions
+ */
+static inline size_t
+binder_alloc_get_free_async_space(struct binder_alloc *alloc)
+{
+ size_t free_async_space;
+
+ mutex_lock(&alloc->mutex);
+ free_async_space = alloc->free_async_space;
+ mutex_unlock(&alloc->mutex);
+ return free_async_space;
+}
+
+/**
+ * binder_alloc_get_user_buffer_offset() - get offset between kernel/user addrs
+ * @alloc: binder_alloc for this proc
+ *
+ * Return: the offset between kernel and user-space addresses to use for
+ * virtual address conversion
+ */
+static inline ptrdiff_t
+binder_alloc_get_user_buffer_offset(struct binder_alloc *alloc)
+{
+ /*
+ * user_buffer_offset is constant if vma is set and
+ * undefined if vma is not set. It is possible to
+ * get here with !alloc->vma if the target process
+ * is dying while a transaction is being initiated.
+ * Returning the old value is ok in this case and
+ * the transaction will fail.
+ */
+ return alloc->user_buffer_offset;
+}
+
+#endif /* _LINUX_BINDER_ALLOC_H */
+
diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h
index 7f20f3dc8369..7967db16ba5a 100644
--- a/drivers/android/binder_trace.h
+++ b/drivers/android/binder_trace.h
@@ -23,7 +23,8 @@
struct binder_buffer;
struct binder_node;
struct binder_proc;
-struct binder_ref;
+struct binder_alloc;
+struct binder_ref_data;
struct binder_thread;
struct binder_transaction;
@@ -146,8 +147,8 @@ TRACE_EVENT(binder_transaction_received,
TRACE_EVENT(binder_transaction_node_to_ref,
TP_PROTO(struct binder_transaction *t, struct binder_node *node,
- struct binder_ref *ref),
- TP_ARGS(t, node, ref),
+ struct binder_ref_data *rdata),
+ TP_ARGS(t, node, rdata),
TP_STRUCT__entry(
__field(int, debug_id)
@@ -160,8 +161,8 @@ TRACE_EVENT(binder_transaction_node_to_ref,
__entry->debug_id = t->debug_id;
__entry->node_debug_id = node->debug_id;
__entry->node_ptr = node->ptr;
- __entry->ref_debug_id = ref->debug_id;
- __entry->ref_desc = ref->desc;
+ __entry->ref_debug_id = rdata->debug_id;
+ __entry->ref_desc = rdata->desc;
),
TP_printk("transaction=%d node=%d src_ptr=0x%016llx ==> dest_ref=%d dest_desc=%d",
__entry->debug_id, __entry->node_debug_id,
@@ -170,8 +171,9 @@ TRACE_EVENT(binder_transaction_node_to_ref,
);
TRACE_EVENT(binder_transaction_ref_to_node,
- TP_PROTO(struct binder_transaction *t, struct binder_ref *ref),
- TP_ARGS(t, ref),
+ TP_PROTO(struct binder_transaction *t, struct binder_node *node,
+ struct binder_ref_data *rdata),
+ TP_ARGS(t, node, rdata),
TP_STRUCT__entry(
__field(int, debug_id)
@@ -182,10 +184,10 @@ TRACE_EVENT(binder_transaction_ref_to_node,
),
TP_fast_assign(
__entry->debug_id = t->debug_id;
- __entry->ref_debug_id = ref->debug_id;
- __entry->ref_desc = ref->desc;
- __entry->node_debug_id = ref->node->debug_id;
- __entry->node_ptr = ref->node->ptr;
+ __entry->ref_debug_id = rdata->debug_id;
+ __entry->ref_desc = rdata->desc;
+ __entry->node_debug_id = node->debug_id;
+ __entry->node_ptr = node->ptr;
),
TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%016llx",
__entry->debug_id, __entry->node_debug_id,
@@ -194,9 +196,10 @@ TRACE_EVENT(binder_transaction_ref_to_node,
);
TRACE_EVENT(binder_transaction_ref_to_ref,
- TP_PROTO(struct binder_transaction *t, struct binder_ref *src_ref,
- struct binder_ref *dest_ref),
- TP_ARGS(t, src_ref, dest_ref),
+ TP_PROTO(struct binder_transaction *t, struct binder_node *node,
+ struct binder_ref_data *src_ref,
+ struct binder_ref_data *dest_ref),
+ TP_ARGS(t, node, src_ref, dest_ref),
TP_STRUCT__entry(
__field(int, debug_id)
@@ -208,7 +211,7 @@ TRACE_EVENT(binder_transaction_ref_to_ref,
),
TP_fast_assign(
__entry->debug_id = t->debug_id;
- __entry->node_debug_id = src_ref->node->debug_id;
+ __entry->node_debug_id = node->debug_id;
__entry->src_ref_debug_id = src_ref->debug_id;
__entry->src_ref_desc = src_ref->desc;
__entry->dest_ref_debug_id = dest_ref->debug_id;
@@ -268,9 +271,9 @@ DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release,
TP_ARGS(buffer));
TRACE_EVENT(binder_update_page_range,
- TP_PROTO(struct binder_proc *proc, bool allocate,
+ TP_PROTO(struct binder_alloc *alloc, bool allocate,
void *start, void *end),
- TP_ARGS(proc, allocate, start, end),
+ TP_ARGS(alloc, allocate, start, end),
TP_STRUCT__entry(
__field(int, proc)
__field(bool, allocate)
@@ -278,9 +281,9 @@ TRACE_EVENT(binder_update_page_range,
__field(size_t, size)
),
TP_fast_assign(
- __entry->proc = proc->pid;
+ __entry->proc = alloc->pid;
__entry->allocate = allocate;
- __entry->offset = start - proc->buffer;
+ __entry->offset = start - alloc->buffer;
__entry->size = end - start;
),
TP_printk("proc=%d allocate=%d offset=%zu size=%zu",
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index e417e1a1d02c..5b2aee83d776 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -2832,10 +2832,12 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
static struct ata_device *ata_find_dev(struct ata_port *ap, int devno)
{
if (!sata_pmp_attached(ap)) {
- if (likely(devno < ata_link_max_devices(&ap->link)))
+ if (likely(devno >= 0 &&
+ devno < ata_link_max_devices(&ap->link)))
return &ap->link.device[devno];
} else {
- if (likely(devno < ap->nr_pmp_links))
+ if (likely(devno >= 0 &&
+ devno < ap->nr_pmp_links))
return &ap->pmp_link[devno].device[0];
}
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 3fa9096b27c2..f3d395bfe8f6 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1116,13 +1116,7 @@ int device_add(struct device *dev)
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
- if ((dev->pm_domain) || (dev->type && dev->type->pm)
- || (dev->class && (dev->class->pm || dev->class->resume))
- || (dev->bus && (dev->bus->pm || dev->bus->resume)) ||
- (dev->driver && dev->driver->pm)) {
- device_pm_add(dev);
- }
-
+ device_pm_add(dev);
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
@@ -2105,7 +2099,11 @@ void device_shutdown(void)
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
- if (dev->bus && dev->bus->shutdown) {
+ if (dev->class && dev->class->shutdown) {
+ if (initcall_debug)
+ dev_info(dev, "shutdown\n");
+ dev->class->shutdown(dev);
+ } else if (dev->bus && dev->bus->shutdown) {
if (initcall_debug)
dev_info(dev, "shutdown\n");
dev->bus->shutdown(dev);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 9920916a6220..ae7f3ce90bd2 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -827,7 +827,7 @@ static ssize_t driver_override_store(struct device *dev,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
- char *driver_override, *old = pdev->driver_override, *cp;
+ char *driver_override, *old, *cp;
if (count > PATH_MAX)
return -EINVAL;
@@ -840,12 +840,15 @@ static ssize_t driver_override_store(struct device *dev,
if (cp)
*cp = '\0';
+ device_lock(dev);
+ old = pdev->driver_override;
if (strlen(driver_override)) {
pdev->driver_override = driver_override;
} else {
kfree(driver_override);
pdev->driver_override = NULL;
}
+ device_unlock(dev);
kfree(old);
@@ -856,8 +859,12 @@ static ssize_t driver_override_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
+ ssize_t len;
- return sprintf(buf, "%s\n", pdev->driver_override);
+ device_lock(dev);
+ len = sprintf(buf, "%s\n", pdev->driver_override);
+ device_unlock(dev);
+ return len;
}
static DEVICE_ATTR_RW(driver_override);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index a48824deabc5..78b0ece0c867 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1188,7 +1188,6 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev,
}
dev->power.subsys_data->domain_data = &gpd_data->base;
- dev->pm_domain = &genpd->domain;
spin_unlock_irq(&dev->power.lock);
@@ -1207,7 +1206,6 @@ static void genpd_free_dev_data(struct device *dev,
{
spin_lock_irq(&dev->power.lock);
- dev->pm_domain = NULL;
dev->power.subsys_data->domain_data = NULL;
spin_unlock_irq(&dev->power.lock);
@@ -1248,6 +1246,8 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (ret)
goto out;
+ dev->pm_domain = &genpd->domain;
+
genpd->device_count++;
genpd->max_off_time_changed = true;
@@ -1299,6 +1299,8 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
if (genpd->detach_dev)
genpd->detach_dev(genpd, dev);
+ dev->pm_domain = NULL;
+
list_del_init(&pdd->list_node);
mutex_unlock(&genpd->lock);
@@ -1373,7 +1375,7 @@ EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain);
int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *subdomain)
{
- struct gpd_link *link;
+ struct gpd_link *l, *link;
int ret = -EINVAL;
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
@@ -1388,7 +1390,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
goto out;
}
- list_for_each_entry(link, &genpd->master_links, master_node) {
+ list_for_each_entry_safe(link, l, &genpd->master_links, master_node) {
if (link->slave != subdomain)
continue;
@@ -1642,10 +1644,10 @@ EXPORT_SYMBOL_GPL(__of_genpd_add_provider);
*/
void of_genpd_del_provider(struct device_node *np)
{
- struct of_genpd_provider *cp;
+ struct of_genpd_provider *cp, *tmp;
mutex_lock(&of_genpd_mutex);
- list_for_each_entry(cp, &of_genpd_providers, link) {
+ list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) {
if (cp->node == np) {
list_del(&cp->link);
of_node_put(cp->node);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index a88590bb0b10..6c5bc3fadfcf 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -162,12 +162,6 @@ void device_pm_move_before(struct device *deva, struct device *devb)
pr_debug("PM: Moving %s:%s before %s:%s\n",
deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
- if (!((devb->pm_domain) || (devb->type && devb->type->pm)
- || (devb->class && (devb->class->pm || devb->class->resume))
- || (devb->bus && (devb->bus->pm || devb->bus->resume)) ||
- (devb->driver && devb->driver->pm))) {
- device_pm_add(devb);
- }
/* Delete deva from dpm_list and reinsert before devb. */
list_move_tail(&deva->power.entry, &devb->power.entry);
}
@@ -182,12 +176,6 @@ void device_pm_move_after(struct device *deva, struct device *devb)
pr_debug("PM: Moving %s:%s after %s:%s\n",
deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
- if (!((devb->pm_domain) || (devb->type && devb->type->pm)
- || (devb->class && (devb->class->pm || devb->class->resume))
- || (devb->bus && (devb->bus->pm || devb->bus->resume)) ||
- (devb->driver && devb->driver->pm))) {
- device_pm_add(devb);
- }
/* Delete deva from dpm_list and reinsert after devb. */
list_move(&deva->power.entry, &devb->power.entry);
}
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index a7b46798c81d..39efa7e6c0c0 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -268,6 +268,8 @@ static ssize_t pm_qos_latency_tolerance_store(struct device *dev,
value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT;
else if (!strcmp(buf, "any") || !strcmp(buf, "any\n"))
value = PM_QOS_LATENCY_ANY;
+ else
+ return -EINVAL;
}
ret = dev_pm_qos_update_user_latency_tolerance(dev, value);
return ret < 0 ? ret : n;
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 0e494108c20c..7af116e12e53 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -61,6 +61,8 @@ static LIST_HEAD(wakeup_sources);
static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue);
+DEFINE_STATIC_SRCU(wakeup_srcu);
+
static struct wakeup_source deleted_ws = {
.name = "deleted",
.lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock),
@@ -199,7 +201,7 @@ void wakeup_source_remove(struct wakeup_source *ws)
spin_lock_irqsave(&events_lock, flags);
list_del_rcu(&ws->entry);
spin_unlock_irqrestore(&events_lock, flags);
- synchronize_rcu();
+ synchronize_srcu(&wakeup_srcu);
}
EXPORT_SYMBOL_GPL(wakeup_source_remove);
@@ -331,13 +333,14 @@ void device_wakeup_detach_irq(struct device *dev)
void device_wakeup_arm_wake_irqs(void)
{
struct wakeup_source *ws;
+ int srcuidx;
- rcu_read_lock();
+ srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
if (ws->wakeirq)
dev_pm_arm_wake_irq(ws->wakeirq);
}
- rcu_read_unlock();
+ srcu_read_unlock(&wakeup_srcu, srcuidx);
}
/**
@@ -348,13 +351,14 @@ void device_wakeup_arm_wake_irqs(void)
void device_wakeup_disarm_wake_irqs(void)
{
struct wakeup_source *ws;
+ int srcuidx;
- rcu_read_lock();
+ srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
if (ws->wakeirq)
dev_pm_disarm_wake_irq(ws->wakeirq);
}
- rcu_read_unlock();
+ srcu_read_unlock(&wakeup_srcu, srcuidx);
}
/**
@@ -839,10 +843,10 @@ EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources);
void pm_print_active_wakeup_sources(void)
{
struct wakeup_source *ws;
- int active = 0;
+ int srcuidx, active = 0;
struct wakeup_source *last_activity_ws = NULL;
- rcu_read_lock();
+ srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
if (ws->active) {
pr_info("active wakeup source: %s\n", ws->name);
@@ -858,7 +862,7 @@ void pm_print_active_wakeup_sources(void)
if (!active && last_activity_ws)
pr_info("last active wakeup source: %s\n",
last_activity_ws->name);
- rcu_read_unlock();
+ srcu_read_unlock(&wakeup_srcu, srcuidx);
}
EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources);
@@ -985,8 +989,9 @@ void pm_wakep_autosleep_enabled(bool set)
{
struct wakeup_source *ws;
ktime_t now = ktime_get();
+ int srcuidx;
- rcu_read_lock();
+ srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
spin_lock_irq(&ws->lock);
if (ws->autosleep_enabled != set) {
@@ -1000,7 +1005,7 @@ void pm_wakep_autosleep_enabled(bool set)
}
spin_unlock_irq(&ws->lock);
}
- rcu_read_unlock();
+ srcu_read_unlock(&wakeup_srcu, srcuidx);
}
#endif /* CONFIG_PM_AUTOSLEEP */
@@ -1061,15 +1066,16 @@ static int print_wakeup_source_stats(struct seq_file *m,
static int wakeup_sources_stats_show(struct seq_file *m, void *unused)
{
struct wakeup_source *ws;
+ int srcuidx;
seq_puts(m, "name\t\t\t\t\tactive_count\tevent_count\twakeup_count\t"
"expire_count\tactive_since\ttotal_time\tmax_time\t"
"last_change\tprevent_suspend_time\n");
- rcu_read_lock();
+ srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry)
print_wakeup_source_stats(m, ws);
- rcu_read_unlock();
+ srcu_read_unlock(&wakeup_srcu, srcuidx);
print_wakeup_source_stats(m, &deleted_ws);
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 6ca35495a5be..1e5cd39d0cc2 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -641,11 +641,12 @@ static int virtblk_probe(struct virtio_device *vdev)
if (err)
goto out_put_disk;
- q = vblk->disk->queue = blk_mq_init_queue(&vblk->tag_set);
+ q = blk_mq_init_queue(&vblk->tag_set);
if (IS_ERR(q)) {
err = -ENOMEM;
goto out_free_tags;
}
+ vblk->disk->queue = q;
q->queuedata = vblk;
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index 41fb1a917b17..33e23a7a691f 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -595,8 +595,6 @@ int xen_blkif_schedule(void *arg)
unsigned long timeout;
int ret;
- xen_blkif_get(blkif);
-
while (!kthread_should_stop()) {
if (try_to_freeze())
continue;
@@ -650,7 +648,6 @@ purge_gnt_list:
print_stats(blkif);
blkif->xenblkd = NULL;
- xen_blkif_put(blkif);
return 0;
}
diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c
index f53cff42f8da..923308201375 100644
--- a/drivers/block/xen-blkback/xenbus.c
+++ b/drivers/block/xen-blkback/xenbus.c
@@ -221,7 +221,6 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif)
if (blkif->xenblkd) {
kthread_stop(blkif->xenblkd);
wake_up(&blkif->shutdown_wq);
- blkif->xenblkd = NULL;
}
/* The above kthread_stop() guarantees that at this point we
@@ -266,9 +265,10 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif)
static void xen_blkif_free(struct xen_blkif *blkif)
{
-
- xen_blkif_disconnect(blkif);
+ WARN_ON(xen_blkif_disconnect(blkif));
xen_vbd_free(&blkif->vbd);
+ kfree(blkif->be->mode);
+ kfree(blkif->be);
/* Make sure everything is drained before shutting down */
BUG_ON(blkif->persistent_gnt_c != 0);
@@ -445,8 +445,6 @@ static int xen_blkbk_remove(struct xenbus_device *dev)
xen_blkif_put(be->blkif);
}
- kfree(be->mode);
- kfree(be);
return 0;
}
diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c
index 969f755f5dc4..0a61186167ba 100644
--- a/drivers/bluetooth/btfm_slim.c
+++ b/drivers/bluetooth/btfm_slim.c
@@ -155,23 +155,22 @@ int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
rxport, 1);
if (ret < 0) {
BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
- ret);
+ ret);
goto error;
}
}
if (rxport) {
BTFMSLIM_INFO("slim_connect_sink(port: %d, ch: %d)",
- ch->port, ch->ch);
+ ch->port, ch->ch);
/* Connect Port with channel given by Machine driver*/
ret = slim_connect_sink(btfmslim->slim_pgd,
&ch->port_hdl, 1, ch->ch_hdl);
if (ret < 0) {
BTFMSLIM_ERR("slim_connect_sink failed ret[%d]",
- ret);
+ ret);
goto remove_channel;
}
-
} else {
BTFMSLIM_INFO("slim_connect_src(port: %d, ch: %d)",
ch->port, ch->ch);
@@ -180,7 +179,7 @@ int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
ch->ch_hdl);
if (ret < 0) {
BTFMSLIM_ERR("slim_connect_src failed ret[%d]",
- ret);
+ ret);
goto remove_channel;
}
}
@@ -190,6 +189,7 @@ int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
BTFMSLIM_INFO(
"port: %d, ch: %d, grp: %d, ch->grph: 0x%x, ch_hdl: 0x%x",
chan->port, chan->ch, grp, chan->grph, chan->ch_hdl);
+
ret = slim_control_ch(btfmslim->slim_pgd, (grp ? chan->grph :
chan->ch_hdl), SLIM_CH_ACTIVATE, true);
if (ret < 0) {
@@ -220,6 +220,7 @@ int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
BTFMSLIM_INFO("port:%d, grp: %d, ch->grph:0x%x, ch->ch_hdl:0x%x ",
ch->port, grp, ch->grph, ch->ch_hdl);
+
/* Remove the channel immediately*/
ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
SLIM_CH_REMOVE, true);
@@ -233,7 +234,6 @@ int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
goto error;
}
}
-
/* Disable port through registration setting */
for (i = 0; i < nchan; i++, ch++) {
if (btfmslim->vendor_port_en) {
@@ -246,9 +246,11 @@ int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
}
}
}
+
error:
return ret;
}
+
static int btfm_slim_get_logical_addr(struct slim_device *slim)
{
int ret = 0;
diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c
index 96be0e2f9183..035e8d9fb5fd 100644
--- a/drivers/bluetooth/btfm_slim_codec.c
+++ b/drivers/bluetooth/btfm_slim_codec.c
@@ -118,9 +118,6 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream,
return;
}
- if (dai->id == BTFM_FM_SLIM_TX)
- goto out;
-
/* Search for dai->id matched port handler */
for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
(ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
@@ -134,7 +131,6 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream,
}
btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan);
-out:
btfm_slim_hw_deinit(btfmslim);
}
@@ -205,61 +201,6 @@ int btfm_slim_dai_prepare(struct snd_pcm_substream *substream,
return ret;
}
-static int btfm_slim_dai_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- int ret = -EINVAL, i;
- struct btfmslim *btfmslim = dai->dev->platform_data;
- struct btfmslim_ch *ch;
- uint8_t rxport, grp = false, nchan = 1;
-
- BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
- dai->id, dai->rate);
-
- switch (dai->id) {
- case BTFM_FM_SLIM_TX:
- grp = true; nchan = 2;
- ch = btfmslim->tx_chs;
- rxport = 0;
- break;
- case BTFM_BT_SCO_SLIM_TX:
- ch = btfmslim->tx_chs;
- rxport = 0;
- break;
- case BTFM_BT_SCO_A2DP_SLIM_RX:
- case BTFM_BT_SPLIT_A2DP_SLIM_RX:
- ch = btfmslim->rx_chs;
- rxport = 1;
- break;
- case BTFM_SLIM_NUM_CODEC_DAIS:
- default:
- BTFMSLIM_ERR("dai->id is invalid:%d", dai->id);
- goto out;
- }
-
- if (dai->id != BTFM_FM_SLIM_TX) {
- ret = 0;
- goto out;
- }
-
- /* Search for dai->id matched port handler */
- for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
- (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
- (ch->id != dai->id); ch++, i++)
- ;
-
- if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
- (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
- BTFMSLIM_ERR("ch is invalid!!");
- goto out;
- }
-
- btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan);
-
-out:
- return ret;
-}
-
/* This function will be called once during boot up */
static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
@@ -402,7 +343,6 @@ static struct snd_soc_dai_ops btfmslim_dai_ops = {
.shutdown = btfm_slim_dai_shutdown,
.hw_params = btfm_slim_dai_hw_params,
.prepare = btfm_slim_dai_prepare,
- .hw_free = btfm_slim_dai_hw_free,
.set_channel_map = btfm_slim_dai_set_channel_map,
.get_channel_map = btfm_slim_dai_get_channel_map,
};
diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c
index 0c4e0b3d5c2e..7abd5598c47b 100644
--- a/drivers/bluetooth/btfm_slim_wcn3990.c
+++ b/drivers/bluetooth/btfm_slim_wcn3990.c
@@ -82,11 +82,12 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
uint8_t rxport, uint8_t enable)
{
int ret = 0;
- uint8_t reg_val = 0;
+ uint8_t reg_val = 0, en;
uint8_t port_bit = 0;
uint16_t reg;
BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable);
+
if (rxport) {
if (enable) {
/* For SCO Rx, A2DP Rx */
@@ -117,7 +118,20 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num);
ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
if (ret) {
- BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
+ BTFMSLIM_ERR("failed to write (%d) reg 0x%x",
+ ret, reg);
+ goto error;
+ }
+ } else if (port_num == CHRK_SB_PGD_PORT_TX_SCO) {
+ /* SCO Tx */
+ reg_val = 0x1 << CHRK_SB_PGD_PORT_TX_SCO;
+ reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num);
+ BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)",
+ reg_val, reg);
+ ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
+ if (ret) {
+ BTFMSLIM_ERR("failed to write (%d) reg 0x%x",
+ ret, reg);
goto error;
}
}
@@ -137,15 +151,21 @@ enable_disable_txport:
reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num);
enable_disable_rxport:
- if (enable) {
- if (is_fm_port(port_num))
- reg_val = CHRK_SB_PGD_PORT_ENABLE |
- CHRK_SB_PGD_PORT_WM_L3;
- else
- reg_val = CHRK_SB_PGD_PORT_ENABLE |
- CHRK_SB_PGD_PORT_WM_LB;
- } else
- reg_val = CHRK_SB_PGD_PORT_DISABLE;
+ if (enable)
+ en = CHRK_SB_PGD_PORT_ENABLE;
+ else
+ en = CHRK_SB_PGD_PORT_DISABLE;
+
+ if (is_fm_port(port_num))
+ reg_val = en | CHRK_SB_PGD_PORT_WM_L8;
+ else if (port_num == CHRK_SB_PGD_PORT_TX_SCO)
+ reg_val = enable ? en | CHRK_SB_PGD_PORT_WM_L1 : en;
+ else
+ reg_val = enable ? en | CHRK_SB_PGD_PORT_WM_LB : en;
+
+ if (enable && port_num == CHRK_SB_PGD_PORT_TX_SCO)
+ BTFMSLIM_INFO("programming SCO Tx with reg_val %d to reg 0x%x",
+ reg_val, reg);
ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
if (ret)
diff --git a/drivers/bluetooth/btfm_slim_wcn3990.h b/drivers/bluetooth/btfm_slim_wcn3990.h
index f6a260096c91..b637ac581201 100644
--- a/drivers/bluetooth/btfm_slim_wcn3990.h
+++ b/drivers/bluetooth/btfm_slim_wcn3990.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -68,6 +68,7 @@
#define CHRK_SB_PGD_PORT_WM_L1 (0x1 << 1)
#define CHRK_SB_PGD_PORT_WM_L2 (0x2 << 1)
#define CHRK_SB_PGD_PORT_WM_L3 (0x3 << 1)
+#define CHRK_SB_PGD_PORT_WM_L8 (0x8 << 1)
#define CHRK_SB_PGD_PORT_WM_LB (0xB << 1)
#define CHRK_SB_PGD_PORT_RX_NUM 16
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 8017961783f7..77c8f279b4f5 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -58,6 +58,7 @@
#define FASTRPC_ENOSUCH 39
#define VMID_SSC_Q6 5
#define VMID_ADSP_Q6 6
+#define AC_VM_ADSP_HEAP_SHARED 33
#define DEBUGFS_SIZE 1024
#define RPC_TIMEOUT (5 * HZ)
@@ -222,6 +223,7 @@ struct fastrpc_channel_ctx {
int prevssrcount;
int issubsystemup;
int vmid;
+ int heap_vmid;
int ramdumpenabled;
void *remoteheap_ramdump_dev;
struct fastrpc_glink_info link;
@@ -1254,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);
@@ -1267,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;
}
@@ -1335,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);
+ }
}
}
@@ -1351,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);
@@ -1384,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,
@@ -1456,7 +1478,7 @@ static void smd_event_handler(void *priv, unsigned event)
switch (event) {
case SMD_EVENT_OPEN:
- complete(&me->channel[cid].work);
+ complete(&me->channel[cid].workport);
break;
case SMD_EVENT_CLOSE:
fastrpc_notify_drivers(me, cid);
@@ -1477,7 +1499,7 @@ static void fastrpc_init(struct fastrpc_apps *me)
me->channel = &gcinfo[0];
for (i = 0; i < NUM_CHANNELS; i++) {
init_completion(&me->channel[i].work);
- init_completion(&me->channel[i].workport);
+ init_completion(&me->channel[i].workport);
me->channel[i].sesscount = 0;
}
}
@@ -1594,7 +1616,7 @@ static int fastrpc_init_process(struct fastrpc_file *fl,
struct fastrpc_mmap *file = 0, *mem = 0;
char *proc_name = NULL;
int srcVM[1] = {VMID_HLOS};
- int destVM[1] = {VMID_ADSP_Q6};
+ int destVM[1] = {gcinfo[0].heap_vmid};
int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
int hlosVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
@@ -1853,7 +1875,7 @@ static int fastrpc_mmap_on_dsp(struct fastrpc_file *fl, uint32_t flags,
} else if (flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
int srcVM[1] = {VMID_HLOS};
- int destVM[1] = {VMID_ADSP_Q6};
+ int destVM[1] = {gcinfo[0].heap_vmid};
int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
VERIFY(err, !hyp_assign_phys(map->phys, (uint64_t)map->size,
@@ -1869,7 +1891,7 @@ static int fastrpc_munmap_on_dsp_rh(struct fastrpc_file *fl,
struct fastrpc_mmap *map)
{
int err = 0;
- int srcVM[1] = {VMID_ADSP_Q6};
+ int srcVM[1] = {gcinfo[0].heap_vmid};
int destVM[1] = {VMID_HLOS};
int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
@@ -2400,16 +2422,16 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer,
spin_lock(&fl->hlock);
hlist_for_each_entry_safe(buf, n, &fl->bufs, hn) {
len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len,
- "%s %p %s %p %s %llx\n", "buf:",
- buf, "buf->virt:", buf->virt,
- "buf->phys:", buf->phys);
+ "%s %pK %s %pK %s %llx\n", "buf:",
+ buf, "buf->virt:", buf->virt,
+ "buf->phys:", buf->phys);
}
len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len,
"\n%s\n",
"LIST OF MAPS:");
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len,
- "%s %p %s %lx %s %llx\n",
+ "%s %pK %s %lx %s %llx\n",
"map:", map,
"map->va:", map->va,
"map->phys:", map->phys);
@@ -2419,7 +2441,7 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer,
"LIST OF PENDING SMQCONTEXTS:");
hlist_for_each_entry_safe(ictx, n, &fl->clst.pending, hn) {
len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len,
- "%s %p %s %u %s %u %s %u\n",
+ "%s %pK %s %u %s %u %s %u\n",
"smqcontext:", ictx,
"sc:", ictx->sc,
"tid:", ictx->pid,
@@ -2430,7 +2452,7 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer,
"LIST OF INTERRUPTED SMQCONTEXTS:");
hlist_for_each_entry_safe(ictx, n, &fl->clst.interrupted, hn) {
len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len,
- "%s %p %s %u %s %u %s %u\n",
+ "%s %pK %s %u %s %u %s %u\n",
"smqcontext:", ictx,
"sc:", ictx->sc,
"tid:", ictx->pid,
@@ -2500,6 +2522,16 @@ static int fastrpc_channel_open(struct fastrpc_file *fl)
kref_init(&me->channel[cid].kref);
pr_info("'opened /dev/%s c %d %d'\n", gcinfo[cid].name,
MAJOR(me->dev_no), cid);
+
+ if (me->glink) {
+ err = glink_queue_rx_intent(me->channel[cid].chan,
+ NULL, 16);
+ err |= glink_queue_rx_intent(me->channel[cid].chan,
+ NULL, 64);
+ if (err)
+ pr_warn("adsprpc: intent fail for %d err %d\n",
+ cid, err);
+ }
if (cid == 0 && me->channel[cid].ssrcount !=
me->channel[cid].prevssrcount) {
if (fastrpc_mmap_remove_ssr(fl))
@@ -2695,6 +2727,10 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num,
VERIFY(err, 0 == copy_from_user(&p.init, param, size));
if (err)
goto bail;
+ VERIFY(err, p.init.init.filelen >= 0 &&
+ p.init.init.memlen >= 0);
+ if (err)
+ goto bail;
VERIFY(err, 0 == fastrpc_init_process(fl, &p.init));
if (err)
goto bail;
@@ -2840,6 +2876,7 @@ static int fastrpc_cb_probe(struct device *dev)
chan->sesscount++;
debugfs_global_file = debugfs_create_file("global", 0644, debugfs_root,
NULL, &debugfs_fops);
+
bail:
return err;
}
@@ -2953,6 +2990,12 @@ static int fastrpc_probe(struct platform_device *pdev)
}
return 0;
}
+ if (of_property_read_bool(dev->of_node,
+ "qcom,fastrpc-vmid-heap-shared"))
+ gcinfo[0].heap_vmid = AC_VM_ADSP_HEAP_SHARED;
+ else
+ gcinfo[0].heap_vmid = VMID_ADSP_Q6;
+ pr_info("ADSPRPC: gcinfo[0].heap_vmid %d\n", gcinfo[0].heap_vmid);
me->glink = of_property_read_bool(dev->of_node, "qcom,fastrpc-glink");
VERIFY(err, !of_platform_populate(pdev->dev.of_node,
fastrpc_match_table,
diff --git a/drivers/char/diag/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 4ae2158b5a6b..10038e629e6c 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -67,7 +67,6 @@ void diag_cntl_channel_close(struct diagfwd_info *p_info)
driver->feature[peripheral].sent_feature_mask = 0;
driver->feature[peripheral].rcvd_feature_mask = 0;
- flush_workqueue(driver->cntl_wq);
reg_dirty |= PERIPHERAL_MASK(peripheral);
diag_cmd_remove_reg_by_proc(peripheral);
driver->feature[peripheral].stm_support = DISABLE_STM;
@@ -1120,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_glink.c b/drivers/char/diag/diagfwd_glink.c
index 42182e3a939d..f1f8f0b2b34b 100644
--- a/drivers/char/diag/diagfwd_glink.c
+++ b/drivers/char/diag/diagfwd_glink.c
@@ -375,8 +375,10 @@ static void diag_glink_notify_rx_work_fn(struct work_struct *work)
struct diag_glink_read_work, work);
struct diag_glink_info *glink_info = read_work->glink_info;
- if (!glink_info || !glink_info->hdl)
+ if (!glink_info || !glink_info->hdl) {
+ kfree(read_work);
return;
+ }
diagfwd_channel_read_done(glink_info->fwd_ctxt,
(unsigned char *)(read_work->ptr_read_done),
@@ -388,6 +390,7 @@ static void diag_glink_notify_rx_work_fn(struct work_struct *work)
"diag: Rx done for packet %pK of len: %d periph: %d ch: %d\n",
read_work->ptr_rx_done, (int)read_work->ptr_read_size,
glink_info->peripheral, glink_info->type);
+ kfree(read_work);
}
static void diag_glink_notify_rx(void *hdl, const void *priv,
@@ -411,6 +414,7 @@ static void diag_glink_notify_rx(void *hdl, const void *priv,
if (!read_work) {
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
"diag: Could not allocate read_work\n");
+ glink_rx_done(glink_info->hdl, ptr, true);
return;
}
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 6860de0d2288..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);
@@ -1285,6 +1277,9 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info)
if (!fwd_info)
return -EIO;
+ if (fwd_info->type == TYPE_CNTL)
+ flush_workqueue(driver->cntl_wq);
+
mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]);
fwd_info->ch_open = 0;
if (fwd_info && fwd_info->c_ops && fwd_info->c_ops->close)
@@ -1342,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/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index a084a4751fa9..25372dc381d4 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -3877,6 +3877,9 @@ static void smi_recv_tasklet(unsigned long val)
* because the lower layer is allowed to hold locks while calling
* message delivery.
*/
+
+ rcu_read_lock();
+
if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
if (intf->curr_msg == NULL && !intf->in_shutdown) {
@@ -3899,6 +3902,8 @@ static void smi_recv_tasklet(unsigned long val)
if (newmsg)
intf->handlers->sender(intf->send_info, newmsg);
+ rcu_read_unlock();
+
handle_new_recv_msgs(intf);
}
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 0d83cfb9708f..f53e8ba2c718 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -758,6 +758,11 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
result, len, data[2]);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_GET_MSG_FLAGS_CMD) {
+ /*
+ * Don't abort here, maybe it was a queued
+ * response to a previous command.
+ */
+ ipmi_ssif_unlock_cond(ssif_info, flags);
pr_warn(PFX "Invalid response getting flags: %x %x\n",
data[0], data[1]);
} else {
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 096f0cef4da1..40d400fe5bb7 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -1162,10 +1162,11 @@ static int wdog_reboot_handler(struct notifier_block *this,
ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
} else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) {
- /* Set a long timer to let the reboot happens, but
- reboot if it hangs, but only if the watchdog
+ /* Set a long timer to let the reboot happen or
+ reset if it hangs, but only if the watchdog
timer was already running. */
- timeout = 120;
+ if (timeout < 120)
+ timeout = 120;
pretimeout = 0;
ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 252142524ff2..a0d9ac6b6cc9 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -29,33 +29,92 @@
#include "tpm.h"
#include "tpm_eventlog.h"
-static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
-static LIST_HEAD(tpm_chip_list);
-static DEFINE_SPINLOCK(driver_lock);
+DEFINE_IDR(dev_nums_idr);
+static DEFINE_MUTEX(idr_lock);
struct class *tpm_class;
dev_t tpm_devt;
-/*
- * tpm_chip_find_get - return tpm_chip for a given chip number
- * @chip_num the device number for the chip
+/**
+ * tpm_try_get_ops() - Get a ref to the tpm_chip
+ * @chip: Chip to ref
+ *
+ * The caller must already have some kind of locking to ensure that chip is
+ * valid. This function will lock the chip so that the ops member can be
+ * accessed safely. The locking prevents tpm_chip_unregister from
+ * completing, so it should not be held for long periods.
+ *
+ * Returns -ERRNO if the chip could not be got.
*/
-struct tpm_chip *tpm_chip_find_get(int chip_num)
+int tpm_try_get_ops(struct tpm_chip *chip)
{
- struct tpm_chip *pos, *chip = NULL;
+ int rc = -EIO;
- rcu_read_lock();
- list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
- if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
- continue;
+ get_device(&chip->dev);
- if (try_module_get(pos->pdev->driver->owner)) {
- chip = pos;
- break;
- }
+ down_read(&chip->ops_sem);
+ if (!chip->ops)
+ goto out_lock;
+
+ if (!try_module_get(chip->dev.parent->driver->owner))
+ goto out_lock;
+
+ return 0;
+out_lock:
+ up_read(&chip->ops_sem);
+ put_device(&chip->dev);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_try_get_ops);
+
+/**
+ * tpm_put_ops() - Release a ref to the tpm_chip
+ * @chip: Chip to put
+ *
+ * This is the opposite pair to tpm_try_get_ops(). After this returns chip may
+ * be kfree'd.
+ */
+void tpm_put_ops(struct tpm_chip *chip)
+{
+ module_put(chip->dev.parent->driver->owner);
+ up_read(&chip->ops_sem);
+ put_device(&chip->dev);
+}
+EXPORT_SYMBOL_GPL(tpm_put_ops);
+
+/**
+ * tpm_chip_find_get() - return tpm_chip for a given chip number
+ * @chip_num: id to find
+ *
+ * The return'd chip has been tpm_try_get_ops'd and must be released via
+ * tpm_put_ops
+ */
+struct tpm_chip *tpm_chip_find_get(int chip_num)
+{
+ struct tpm_chip *chip, *res = NULL;
+ int chip_prev;
+
+ mutex_lock(&idr_lock);
+
+ if (chip_num == TPM_ANY_NUM) {
+ chip_num = 0;
+ do {
+ chip_prev = chip_num;
+ chip = idr_get_next(&dev_nums_idr, &chip_num);
+ if (chip && !tpm_try_get_ops(chip)) {
+ res = chip;
+ break;
+ }
+ } while (chip_prev != chip_num);
+ } else {
+ chip = idr_find_slowpath(&dev_nums_idr, chip_num);
+ if (chip && !tpm_try_get_ops(chip))
+ res = chip;
}
- rcu_read_unlock();
- return chip;
+
+ mutex_unlock(&idr_lock);
+
+ return res;
}
/**
@@ -68,12 +127,48 @@ static void tpm_dev_release(struct device *dev)
{
struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
- spin_lock(&driver_lock);
- clear_bit(chip->dev_num, dev_mask);
- spin_unlock(&driver_lock);
+ mutex_lock(&idr_lock);
+ idr_remove(&dev_nums_idr, chip->dev_num);
+ mutex_unlock(&idr_lock);
+
kfree(chip);
}
+
+/**
+ * tpm_class_shutdown() - prepare the TPM device for loss of power.
+ * @dev: device to which the chip is associated.
+ *
+ * Issues a TPM2_Shutdown command prior to loss of power, as required by the
+ * TPM 2.0 spec.
+ * Then, calls bus- and device- specific shutdown code.
+ *
+ * XXX: This codepath relies on the fact that sysfs is not enabled for
+ * TPM2: sysfs uses an implicit lock on chip->ops, so this could race if TPM2
+ * has sysfs support enabled before TPM sysfs's implicit locking is fixed.
+ */
+static int tpm_class_shutdown(struct device *dev)
+{
+ struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
+
+ if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+ down_write(&chip->ops_sem);
+ tpm2_shutdown(chip, TPM2_SU_CLEAR);
+ chip->ops = NULL;
+ up_write(&chip->ops_sem);
+ }
+ /* Allow bus- and device-specific code to run. Note: since chip->ops
+ * is NULL, more-specific shutdown code will not be able to issue TPM
+ * commands.
+ */
+ if (dev->bus && dev->bus->shutdown)
+ dev->bus->shutdown(dev);
+ else if (dev->driver && dev->driver->shutdown)
+ dev->driver->shutdown(dev);
+ return 0;
+}
+
+
/**
* tpmm_chip_alloc() - allocate a new struct tpm_chip instance
* @dev: device to which the chip is associated
@@ -88,37 +183,35 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
const struct tpm_class_ops *ops)
{
struct tpm_chip *chip;
+ int rc;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return ERR_PTR(-ENOMEM);
mutex_init(&chip->tpm_mutex);
- INIT_LIST_HEAD(&chip->list);
+ init_rwsem(&chip->ops_sem);
chip->ops = ops;
- spin_lock(&driver_lock);
- chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
- spin_unlock(&driver_lock);
-
- if (chip->dev_num >= TPM_NUM_DEVICES) {
+ mutex_lock(&idr_lock);
+ rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL);
+ mutex_unlock(&idr_lock);
+ if (rc < 0) {
dev_err(dev, "No available tpm device numbers\n");
kfree(chip);
- return ERR_PTR(-ENOMEM);
+ return ERR_PTR(rc);
}
-
- set_bit(chip->dev_num, dev_mask);
+ chip->dev_num = rc;
scnprintf(chip->devname, sizeof(chip->devname), "tpm%d", chip->dev_num);
- chip->pdev = dev;
-
dev_set_drvdata(dev, chip);
chip->dev.class = tpm_class;
+ chip->dev.class->shutdown = tpm_class_shutdown;
chip->dev.release = tpm_dev_release;
- chip->dev.parent = chip->pdev;
+ chip->dev.parent = dev;
#ifdef CONFIG_ACPI
chip->dev.groups = chip->groups;
#endif
@@ -133,7 +226,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
device_initialize(&chip->dev);
cdev_init(&chip->cdev, &tpm_fops);
- chip->cdev.owner = chip->pdev->driver->owner;
+ chip->cdev.owner = dev->driver->owner;
chip->cdev.kobj.parent = &chip->dev.kobj;
devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev);
@@ -167,6 +260,11 @@ static int tpm_add_char_device(struct tpm_chip *chip)
return rc;
}
+ /* Make the chip available. */
+ mutex_lock(&idr_lock);
+ idr_replace(&dev_nums_idr, chip, chip->dev_num);
+ mutex_unlock(&idr_lock);
+
return rc;
}
@@ -174,6 +272,16 @@ static void tpm_del_char_device(struct tpm_chip *chip)
{
cdev_del(&chip->cdev);
device_del(&chip->dev);
+
+ /* Make the chip unavailable. */
+ mutex_lock(&idr_lock);
+ idr_replace(&dev_nums_idr, NULL, chip->dev_num);
+ mutex_unlock(&idr_lock);
+
+ /* Make the driver uncallable. */
+ down_write(&chip->ops_sem);
+ chip->ops = NULL;
+ up_write(&chip->ops_sem);
}
static int tpm1_chip_register(struct tpm_chip *chip)
@@ -228,17 +336,11 @@ int tpm_chip_register(struct tpm_chip *chip)
if (rc)
goto out_err;
- /* Make the chip available. */
- spin_lock(&driver_lock);
- list_add_tail_rcu(&chip->list, &tpm_chip_list);
- spin_unlock(&driver_lock);
-
chip->flags |= TPM_CHIP_FLAG_REGISTERED;
if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
- rc = __compat_only_sysfs_link_entry_to_kobj(&chip->pdev->kobj,
- &chip->dev.kobj,
- "ppi");
+ rc = __compat_only_sysfs_link_entry_to_kobj(
+ &chip->dev.parent->kobj, &chip->dev.kobj, "ppi");
if (rc && rc != -ENOENT) {
tpm_chip_unregister(chip);
return rc;
@@ -259,6 +361,9 @@ EXPORT_SYMBOL_GPL(tpm_chip_register);
* Takes the chip first away from the list of available TPM chips and then
* cleans up all the resources reserved by tpm_chip_register().
*
+ * Once this function returns the driver call backs in 'op's will not be
+ * running and will no longer start.
+ *
* NOTE: This function should be only called before deinitializing chip
* resources.
*/
@@ -267,13 +372,8 @@ void tpm_chip_unregister(struct tpm_chip *chip)
if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED))
return;
- spin_lock(&driver_lock);
- list_del_rcu(&chip->list);
- spin_unlock(&driver_lock);
- synchronize_rcu();
-
if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
- sysfs_remove_link(&chip->pdev->kobj, "ppi");
+ sysfs_remove_link(&chip->dev.parent->kobj, "ppi");
tpm1_chip_unregister(chip);
tpm_del_char_device(chip);
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index 4f3137d9a35e..912ad30be585 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -61,7 +61,7 @@ static int tpm_open(struct inode *inode, struct file *file)
* by the check of is_open variable, which is protected
* by driver_lock. */
if (test_and_set_bit(0, &chip->is_open)) {
- dev_dbg(chip->pdev, "Another process owns this TPM\n");
+ dev_dbg(&chip->dev, "Another process owns this TPM\n");
return -EBUSY;
}
@@ -79,7 +79,6 @@ static int tpm_open(struct inode *inode, struct file *file)
INIT_WORK(&priv->work, timeout_work);
file->private_data = priv;
- get_device(chip->pdev);
return 0;
}
@@ -137,9 +136,18 @@ static ssize_t tpm_write(struct file *file, const char __user *buf,
return -EFAULT;
}
- /* atomic tpm command send and result receive */
+ /* atomic tpm command send and result receive. We only hold the ops
+ * lock during this period so that the tpm can be unregistered even if
+ * the char dev is held open.
+ */
+ if (tpm_try_get_ops(priv->chip)) {
+ mutex_unlock(&priv->buffer_mutex);
+ return -EPIPE;
+ }
out_size = tpm_transmit(priv->chip, priv->data_buffer,
sizeof(priv->data_buffer), 0);
+
+ tpm_put_ops(priv->chip);
if (out_size < 0) {
mutex_unlock(&priv->buffer_mutex);
return out_size;
@@ -166,7 +174,6 @@ static int tpm_release(struct inode *inode, struct file *file)
file->private_data = NULL;
atomic_set(&priv->data_pending, 0);
clear_bit(0, &priv->chip->is_open);
- put_device(priv->chip->pdev);
kfree(priv);
return 0;
}
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 17abe52e6365..aaa5fa95dede 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -343,7 +343,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
if (count == 0)
return -ENODATA;
if (count > bufsiz) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"invalid count value %x %zx\n", count, bufsiz);
return -E2BIG;
}
@@ -353,7 +353,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
rc = chip->ops->send(chip, (u8 *) buf, count);
if (rc < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"tpm_transmit: tpm_send: error %zd\n", rc);
goto out;
}
@@ -372,7 +372,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
goto out_recv;
if (chip->ops->req_canceled(chip, status)) {
- dev_err(chip->pdev, "Operation Canceled\n");
+ dev_err(&chip->dev, "Operation Canceled\n");
rc = -ECANCELED;
goto out;
}
@@ -382,14 +382,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
} while (time_before(jiffies, stop));
chip->ops->cancel(chip);
- dev_err(chip->pdev, "Operation Timed out\n");
+ dev_err(&chip->dev, "Operation Timed out\n");
rc = -ETIME;
goto out;
out_recv:
rc = chip->ops->recv(chip, (u8 *) buf, bufsiz);
if (rc < 0)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"tpm_transmit: tpm_recv: error %zd\n", rc);
out:
if (!(flags & TPM_TRANSMIT_UNLOCKED))
@@ -416,7 +416,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd,
err = be32_to_cpu(header->return_code);
if (err != 0 && desc)
- dev_err(chip->pdev, "A TPM error (%d) occurred %s\n", err,
+ dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err,
desc);
return err;
@@ -514,7 +514,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
if (rc == TPM_ERR_INVALID_POSTINIT) {
/* The TPM is not started, we are the first to talk to it.
Execute a startup command. */
- dev_info(chip->pdev, "Issuing TPM_STARTUP");
+ dev_info(&chip->dev, "Issuing TPM_STARTUP");
if (tpm_startup(chip, TPM_ST_CLEAR))
return rc;
@@ -526,7 +526,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
0, NULL);
}
if (rc) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"A TPM error (%zd) occurred attempting to determine the timeouts\n",
rc);
goto duration;
@@ -565,7 +565,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
/* Report adjusted timeouts */
if (chip->vendor.timeout_adjusted) {
- dev_info(chip->pdev,
+ dev_info(&chip->dev,
HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
old_timeout[0], new_timeout[0],
old_timeout[1], new_timeout[1],
@@ -612,7 +612,7 @@ duration:
chip->vendor.duration[TPM_MEDIUM] *= 1000;
chip->vendor.duration[TPM_LONG] *= 1000;
chip->vendor.duration_adjusted = true;
- dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
+ dev_info(&chip->dev, "Adjusting TPM timeout parameters.");
}
return 0;
}
@@ -687,7 +687,7 @@ int tpm_is_tpm2(u32 chip_num)
rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0;
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
@@ -716,7 +716,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
rc = tpm2_pcr_read(chip, pcr_idx, res_buf);
else
rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_pcr_read);
@@ -751,7 +751,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
rc = tpm2_pcr_extend(chip, pcr_idx, hash);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
@@ -761,7 +761,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0,
"attempting extend a PCR value");
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_pcr_extend);
@@ -802,7 +802,9 @@ int tpm_do_selftest(struct tpm_chip *chip)
* around 300ms while the self test is ongoing, keep trying
* until the self test duration expires. */
if (rc == -ETIME) {
- dev_info(chip->pdev, HW_ERR "TPM command timed out during continue self test");
+ dev_info(
+ &chip->dev, HW_ERR
+ "TPM command timed out during continue self test");
msleep(delay_msec);
continue;
}
@@ -812,7 +814,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
rc = be32_to_cpu(cmd.header.out.return_code);
if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) {
- dev_info(chip->pdev,
+ dev_info(&chip->dev,
"TPM is disabled/deactivated (0x%X)\n", rc);
/* TPM is disabled and/or deactivated; driver can
* proceed and TPM does handle commands for
@@ -840,7 +842,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
rc = tpm_transmit_cmd(chip, cmd, buflen, 0, "attempting tpm_cmd");
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_send);
@@ -966,10 +968,10 @@ int tpm_pm_suspend(struct device *dev)
}
if (rc)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"Error (%d) sending savestate before suspend\n", rc);
else if (try > 0)
- dev_warn(chip->pdev, "TPM savestate took %dms\n",
+ dev_warn(&chip->dev, "TPM savestate took %dms\n",
try * TPM_TIMEOUT_RETRY);
return rc;
@@ -1023,7 +1025,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
err = tpm2_get_random(chip, out, max);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return err;
}
@@ -1045,7 +1047,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
num_bytes -= recd;
} while (retries-- && total < max);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return total ? total : -EIO;
}
EXPORT_SYMBOL_GPL(tpm_get_random);
@@ -1071,7 +1073,7 @@ int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload,
rc = tpm2_seal_trusted(chip, payload, options);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_seal_trusted);
@@ -1097,7 +1099,8 @@ int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload,
rc = tpm2_unseal_trusted(chip, payload, options);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
+
return rc;
}
EXPORT_SYMBOL_GPL(tpm_unseal_trusted);
@@ -1124,6 +1127,7 @@ static int __init tpm_init(void)
static void __exit tpm_exit(void)
{
+ idr_destroy(&dev_nums_idr);
class_destroy(tpm_class);
unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES);
}
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index f880856aa75e..06ac6e9657d2 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -38,6 +38,8 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
struct tpm_chip *chip = dev_get_drvdata(dev);
+ memset(&tpm_cmd, 0, sizeof(tpm_cmd));
+
tpm_cmd.header.in = tpm_readpubek_header;
err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, 0,
"attempting to read the PUBEK");
@@ -284,16 +286,28 @@ static const struct attribute_group tpm_dev_group = {
int tpm_sysfs_add_device(struct tpm_chip *chip)
{
int err;
- err = sysfs_create_group(&chip->pdev->kobj,
+
+ /* XXX: If you wish to remove this restriction, you must first update
+ * tpm_sysfs to explicitly lock chip->ops.
+ */
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ return 0;
+
+ err = sysfs_create_group(&chip->dev.parent->kobj,
&tpm_dev_group);
if (err)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"failed to create sysfs attributes, %d\n", err);
return err;
}
void tpm_sysfs_del_device(struct tpm_chip *chip)
{
- sysfs_remove_group(&chip->pdev->kobj, &tpm_dev_group);
+ /* The sysfs routines rely on an implicit tpm_try_get_ops, this
+ * function is called before ops is null'd and the sysfs core
+ * synchronizes this removal so that no callbacks are running or can
+ * run again
+ */
+ sysfs_remove_group(&chip->dev.parent->kobj, &tpm_dev_group);
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 2216861f89f1..772d99b3a8e4 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -34,7 +34,7 @@
enum tpm_const {
TPM_MINOR = 224, /* officially assigned */
TPM_BUFSIZE = 4096,
- TPM_NUM_DEVICES = 256,
+ TPM_NUM_DEVICES = 65536,
TPM_RETRY = 50, /* 5 seconds */
};
@@ -171,11 +171,16 @@ enum tpm_chip_flags {
};
struct tpm_chip {
- struct device *pdev; /* Device stuff */
struct device dev;
struct cdev cdev;
+ /* A driver callback under ops cannot be run unless ops_sem is held
+ * (sometimes implicitly, eg for the sysfs code). ops becomes null
+ * when the driver is unregistered, see tpm_try_get_ops.
+ */
+ struct rw_semaphore ops_sem;
const struct tpm_class_ops *ops;
+
unsigned int flags;
int dev_num; /* /dev/tpm# */
@@ -195,17 +200,10 @@ struct tpm_chip {
acpi_handle acpi_dev_handle;
char ppi_version[TPM_PPI_VERSION_LEN + 1];
#endif /* CONFIG_ACPI */
-
- struct list_head list;
};
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
-static inline void tpm_chip_put(struct tpm_chip *chip)
-{
- module_put(chip->pdev->driver->owner);
-}
-
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
@@ -497,6 +495,7 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
extern struct class *tpm_class;
extern dev_t tpm_devt;
extern const struct file_operations tpm_fops;
+extern struct idr dev_nums_idr;
enum tpm_transmit_flags {
TPM_TRANSMIT_UNLOCKED = BIT(0),
@@ -517,6 +516,9 @@ extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
wait_queue_head_t *, bool);
struct tpm_chip *tpm_chip_find_get(int chip_num);
+__must_check int tpm_try_get_ops(struct tpm_chip *chip);
+void tpm_put_ops(struct tpm_chip *chip);
+
extern struct tpm_chip *tpmm_chip_alloc(struct device *dev,
const struct tpm_class_ops *ops);
extern int tpm_chip_register(struct tpm_chip *chip);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index cb7e4f6b70ba..286bd090a488 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -570,7 +570,7 @@ static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
if (rc) {
- dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n",
+ dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
handle);
return;
}
@@ -580,7 +580,7 @@ static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags,
"flushing context");
if (rc)
- dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle,
+ dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle,
rc);
tpm_buf_destroy(&buf);
@@ -753,7 +753,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
* except print the error code on a system failure.
*/
if (rc < 0)
- dev_warn(chip->pdev, "transmit returned %d while stopping the TPM",
+ dev_warn(&chip->dev, "transmit returned %d while stopping the TPM",
rc);
}
EXPORT_SYMBOL_GPL(tpm2_shutdown);
@@ -820,7 +820,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
* immediately. This is a workaround for that.
*/
if (rc == TPM2_RC_TESTING) {
- dev_warn(chip->pdev, "Got RC_TESTING, ignoring\n");
+ dev_warn(&chip->dev, "Got RC_TESTING, ignoring\n");
rc = 0;
}
diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
index dfadad0916a1..a48a878f791d 100644
--- a/drivers/char/tpm/tpm_atmel.c
+++ b/drivers/char/tpm/tpm_atmel.c
@@ -49,7 +49,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
for (i = 0; i < 6; i++) {
status = ioread8(chip->vendor.iobase + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
- dev_err(chip->pdev, "error reading header\n");
+ dev_err(&chip->dev, "error reading header\n");
return -EIO;
}
*buf++ = ioread8(chip->vendor.iobase);
@@ -60,12 +60,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
size = be32_to_cpu(*native_size);
if (count < size) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"Recv size(%d) less than available space\n", size);
for (; i < size; i++) { /* clear the waiting data anyway */
status = ioread8(chip->vendor.iobase + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
- dev_err(chip->pdev, "error reading data\n");
+ dev_err(&chip->dev, "error reading data\n");
return -EIO;
}
}
@@ -76,7 +76,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
for (; i < size; i++) {
status = ioread8(chip->vendor.iobase + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
- dev_err(chip->pdev, "error reading data\n");
+ dev_err(&chip->dev, "error reading data\n");
return -EIO;
}
*buf++ = ioread8(chip->vendor.iobase);
@@ -86,7 +86,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
status = ioread8(chip->vendor.iobase + 1);
if (status & ATML_STATUS_DATA_AVAIL) {
- dev_err(chip->pdev, "data available is stuck\n");
+ dev_err(&chip->dev, "data available is stuck\n");
return -EIO;
}
@@ -97,9 +97,9 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
{
int i;
- dev_dbg(chip->pdev, "tpm_atml_send:\n");
+ dev_dbg(&chip->dev, "tpm_atml_send:\n");
for (i = 0; i < count; i++) {
- dev_dbg(chip->pdev, "%d 0x%x(%d)\n", i, buf[i], buf[i]);
+ dev_dbg(&chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]);
iowrite8(buf[i], chip->vendor.iobase);
}
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
index 8dfb88b9739c..dd8f0eb3170a 100644
--- a/drivers/char/tpm/tpm_i2c_atmel.c
+++ b/drivers/char/tpm/tpm_i2c_atmel.c
@@ -52,7 +52,7 @@ struct priv_data {
static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
struct priv_data *priv = chip->vendor.priv;
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
s32 status;
priv->len = 0;
@@ -62,7 +62,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
status = i2c_master_send(client, buf, len);
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
(int)min_t(size_t, 64, len), buf, len, status);
return status;
@@ -71,7 +71,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
struct priv_data *priv = chip->vendor.priv;
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
struct tpm_output_header *hdr =
(struct tpm_output_header *)priv->buffer;
u32 expected_len;
@@ -88,7 +88,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
return -ENOMEM;
if (priv->len >= expected_len) {
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
(int)min_t(size_t, 64, expected_len), buf, count,
expected_len);
@@ -97,7 +97,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
}
rc = i2c_master_recv(client, buf, expected_len);
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
(int)min_t(size_t, 64, expected_len), buf, count,
expected_len);
@@ -106,13 +106,13 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
static void i2c_atmel_cancel(struct tpm_chip *chip)
{
- dev_err(chip->pdev, "TPM operation cancellation was requested, but is not supported");
+ dev_err(&chip->dev, "TPM operation cancellation was requested, but is not supported");
}
static u8 i2c_atmel_read_status(struct tpm_chip *chip)
{
struct priv_data *priv = chip->vendor.priv;
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
int rc;
/* The TPM fails the I2C read until it is ready, so we do the entire
@@ -125,7 +125,7 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip)
/* Once the TPM has completed the command the command remains readable
* until another command is issued. */
rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s: sts=%d", __func__, rc);
if (rc <= 0)
return 0;
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index 63d5d22e9e60..f2aa99e34b4b 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -446,7 +446,7 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
/* read first 10 bytes, including tag, paramsize, and result */
size = recv_data(chip, buf, TPM_HEADER_SIZE);
if (size < TPM_HEADER_SIZE) {
- dev_err(chip->pdev, "Unable to read header\n");
+ dev_err(&chip->dev, "Unable to read header\n");
goto out;
}
@@ -459,14 +459,14 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
size += recv_data(chip, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE);
if (size < expected) {
- dev_err(chip->pdev, "Unable to read remainder of result\n");
+ dev_err(&chip->dev, "Unable to read remainder of result\n");
size = -ETIME;
goto out;
}
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
- dev_err(chip->pdev, "Error left over data\n");
+ dev_err(&chip->dev, "Error left over data\n");
size = -EIO;
goto out;
}
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index 847f1597fe9b..a1e1474dda30 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -96,13 +96,13 @@ static s32 i2c_nuvoton_write_buf(struct i2c_client *client, u8 offset, u8 size,
/* read TPM_STS register */
static u8 i2c_nuvoton_read_status(struct tpm_chip *chip)
{
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
s32 status;
u8 data;
status = i2c_nuvoton_read_buf(client, TPM_STS, 1, &data);
if (status <= 0) {
- dev_err(chip->pdev, "%s() error return %d\n", __func__,
+ dev_err(&chip->dev, "%s() error return %d\n", __func__,
status);
data = TPM_STS_ERR_VAL;
}
@@ -127,13 +127,13 @@ static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data)
/* write commandReady to TPM_STS register */
static void i2c_nuvoton_ready(struct tpm_chip *chip)
{
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
s32 status;
/* this causes the current command to be aborted */
status = i2c_nuvoton_write_status(client, TPM_STS_COMMAND_READY);
if (status < 0)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"%s() fail to write TPM_STS.commandReady\n", __func__);
}
@@ -212,7 +212,7 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
return 0;
} while (time_before(jiffies, stop));
}
- dev_err(chip->pdev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
+ dev_err(&chip->dev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
value);
return -ETIMEDOUT;
}
@@ -240,7 +240,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
&chip->vendor.read_queue) == 0) {
burst_count = i2c_nuvoton_get_burstcount(client, chip);
if (burst_count < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"%s() fail to read burstCount=%d\n", __func__,
burst_count);
return -EIO;
@@ -249,12 +249,12 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_R,
bytes2read, &buf[size]);
if (rc < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"%s() fail on i2c_nuvoton_read_buf()=%d\n",
__func__, rc);
return -EIO;
}
- dev_dbg(chip->pdev, "%s(%d):", __func__, bytes2read);
+ dev_dbg(&chip->dev, "%s(%d):", __func__, bytes2read);
size += bytes2read;
}
@@ -264,7 +264,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
/* Read TPM command results */
static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
- struct device *dev = chip->pdev;
+ struct device *dev = chip->dev.parent;
struct i2c_client *client = to_i2c_client(dev);
s32 rc;
int expected, status, burst_count, retries, size = 0;
@@ -334,7 +334,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
break;
}
i2c_nuvoton_ready(chip);
- dev_dbg(chip->pdev, "%s() -> %d\n", __func__, size);
+ dev_dbg(&chip->dev, "%s() -> %d\n", __func__, size);
return size;
}
@@ -347,7 +347,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
*/
static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
- struct device *dev = chip->pdev;
+ struct device *dev = chip->dev.parent;
struct i2c_client *client = to_i2c_client(dev);
u32 ordinal;
size_t count = 0;
diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c
index 6c488e635fdd..e3cf9f3545c5 100644
--- a/drivers/char/tpm/tpm_infineon.c
+++ b/drivers/char/tpm/tpm_infineon.c
@@ -195,9 +195,9 @@ static int wait(struct tpm_chip *chip, int wait_for_bit)
}
if (i == TPM_MAX_TRIES) { /* timeout occurs */
if (wait_for_bit == STAT_XFE)
- dev_err(chip->pdev, "Timeout in wait(STAT_XFE)\n");
+ dev_err(&chip->dev, "Timeout in wait(STAT_XFE)\n");
if (wait_for_bit == STAT_RDA)
- dev_err(chip->pdev, "Timeout in wait(STAT_RDA)\n");
+ dev_err(&chip->dev, "Timeout in wait(STAT_RDA)\n");
return -EIO;
}
return 0;
@@ -220,7 +220,7 @@ static void wait_and_send(struct tpm_chip *chip, u8 sendbyte)
static void tpm_wtx(struct tpm_chip *chip)
{
number_of_wtx++;
- dev_info(chip->pdev, "Granting WTX (%02d / %02d)\n",
+ dev_info(&chip->dev, "Granting WTX (%02d / %02d)\n",
number_of_wtx, TPM_MAX_WTX_PACKAGES);
wait_and_send(chip, TPM_VL_VER);
wait_and_send(chip, TPM_CTRL_WTX);
@@ -231,7 +231,7 @@ static void tpm_wtx(struct tpm_chip *chip)
static void tpm_wtx_abort(struct tpm_chip *chip)
{
- dev_info(chip->pdev, "Aborting WTX\n");
+ dev_info(&chip->dev, "Aborting WTX\n");
wait_and_send(chip, TPM_VL_VER);
wait_and_send(chip, TPM_CTRL_WTX_ABORT);
wait_and_send(chip, 0x00);
@@ -257,7 +257,7 @@ recv_begin:
}
if (buf[0] != TPM_VL_VER) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"Wrong transport protocol implementation!\n");
return -EIO;
}
@@ -272,7 +272,7 @@ recv_begin:
}
if ((size == 0x6D00) && (buf[1] == 0x80)) {
- dev_err(chip->pdev, "Error handling on vendor layer!\n");
+ dev_err(&chip->dev, "Error handling on vendor layer!\n");
return -EIO;
}
@@ -284,7 +284,7 @@ recv_begin:
}
if (buf[1] == TPM_CTRL_WTX) {
- dev_info(chip->pdev, "WTX-package received\n");
+ dev_info(&chip->dev, "WTX-package received\n");
if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
tpm_wtx(chip);
goto recv_begin;
@@ -295,14 +295,14 @@ recv_begin:
}
if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
- dev_info(chip->pdev, "WTX-abort acknowledged\n");
+ dev_info(&chip->dev, "WTX-abort acknowledged\n");
return size;
}
if (buf[1] == TPM_CTRL_ERROR) {
- dev_err(chip->pdev, "ERROR-package received:\n");
+ dev_err(&chip->dev, "ERROR-package received:\n");
if (buf[4] == TPM_INF_NAK)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"-> Negative acknowledgement"
" - retransmit command!\n");
return -EIO;
@@ -321,7 +321,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
ret = empty_fifo(chip, 1);
if (ret) {
- dev_err(chip->pdev, "Timeout while clearing FIFO\n");
+ dev_err(&chip->dev, "Timeout while clearing FIFO\n");
return -EIO;
}
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index 289389ecef84..766370bed60c 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -113,7 +113,7 @@ static int nsc_wait_for_ready(struct tpm_chip *chip)
}
while (time_before(jiffies, stop));
- dev_info(chip->pdev, "wait for ready failed\n");
+ dev_info(&chip->dev, "wait for ready failed\n");
return -EBUSY;
}
@@ -129,12 +129,12 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
return -EIO;
if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
- dev_err(chip->pdev, "F0 timeout\n");
+ dev_err(&chip->dev, "F0 timeout\n");
return -EIO;
}
if ((data =
inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
- dev_err(chip->pdev, "not in normal mode (0x%x)\n",
+ dev_err(&chip->dev, "not in normal mode (0x%x)\n",
data);
return -EIO;
}
@@ -143,7 +143,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
for (p = buffer; p < &buffer[count]; p++) {
if (wait_for_stat
(chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"OBF timeout (while reading data)\n");
return -EIO;
}
@@ -154,11 +154,11 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
if ((data & NSC_STATUS_F0) == 0 &&
(wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0)) {
- dev_err(chip->pdev, "F0 not set\n");
+ dev_err(&chip->dev, "F0 not set\n");
return -EIO;
}
if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"expected end of command(0x%x)\n", data);
return -EIO;
}
@@ -189,19 +189,19 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
return -EIO;
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
- dev_err(chip->pdev, "IBF timeout\n");
+ dev_err(&chip->dev, "IBF timeout\n");
return -EIO;
}
outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND);
if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
- dev_err(chip->pdev, "IBR timeout\n");
+ dev_err(&chip->dev, "IBR timeout\n");
return -EIO;
}
for (i = 0; i < count; i++) {
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"IBF timeout (while writing data)\n");
return -EIO;
}
@@ -209,7 +209,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
}
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
- dev_err(chip->pdev, "IBF timeout\n");
+ dev_err(&chip->dev, "IBF timeout\n");
return -EIO;
}
outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index f10a107614b4..7f13221aeb30 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -293,7 +293,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
/* read first 10 bytes, including tag, paramsize, and result */
if ((size =
recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) {
- dev_err(chip->pdev, "Unable to read header\n");
+ dev_err(&chip->dev, "Unable to read header\n");
goto out;
}
@@ -306,7 +306,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
if ((size +=
recv_data(chip, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE)) < expected) {
- dev_err(chip->pdev, "Unable to read remainder of result\n");
+ dev_err(&chip->dev, "Unable to read remainder of result\n");
size = -ETIME;
goto out;
}
@@ -315,7 +315,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
&chip->vendor.int_queue, false);
status = tpm_tis_status(chip);
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
- dev_err(chip->pdev, "Error left over data\n");
+ dev_err(&chip->dev, "Error left over data\n");
size = -EIO;
goto out;
}
@@ -401,7 +401,7 @@ static void disable_interrupts(struct tpm_chip *chip)
iowrite32(intmask,
chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.locality));
- devm_free_irq(chip->pdev, chip->vendor.irq, chip);
+ devm_free_irq(&chip->dev, chip->vendor.irq, chip);
chip->vendor.irq = 0;
}
@@ -463,7 +463,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
msleep(1);
if (!priv->irq_tested) {
disable_interrupts(chip);
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
FW_BUG "TPM interrupt not working, polling instead\n");
}
priv->irq_tested = true;
@@ -533,7 +533,7 @@ static int probe_itpm(struct tpm_chip *chip)
rc = tpm_tis_send_data(chip, cmd_getticks, len);
if (rc == 0) {
- dev_info(chip->pdev, "Detected an iTPM.\n");
+ dev_info(&chip->dev, "Detected an iTPM.\n");
rc = 1;
} else
rc = -EFAULT;
@@ -766,7 +766,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
if (devm_request_irq
(dev, i, tis_int_probe, IRQF_SHARED,
chip->devname, chip) != 0) {
- dev_info(chip->pdev,
+ dev_info(&chip->dev,
"Unable to request irq: %d for probe\n",
i);
continue;
@@ -818,7 +818,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
if (devm_request_irq
(dev, chip->vendor.irq, tis_int_handler, IRQF_SHARED,
chip->devname, chip) != 0) {
- dev_info(chip->pdev,
+ dev_info(&chip->dev,
"Unable to request irq: %d for use\n",
chip->vendor.irq);
chip->vendor.irq = 0;
diff --git a/drivers/clk/msm/clock-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/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index 72a75873b810..eb72217b9b1c 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -397,6 +397,7 @@ struct clk_osm {
u32 acd_extint1_cfg;
u32 acd_autoxfer_ctl;
u32 acd_debugfs_addr;
+ u32 acd_debugfs_addr_size;
bool acd_init;
bool secure_init;
bool red_fsm_en;
@@ -1449,6 +1450,7 @@ static int clk_osm_resources_init(struct platform_device *pdev)
return -ENOMEM;
}
pwrcl_clk.pbases[ACD_BASE] = pbase;
+ pwrcl_clk.acd_debugfs_addr_size = resource_size(res);
pwrcl_clk.vbases[ACD_BASE] = vbase;
pwrcl_clk.acd_init = true;
} else {
@@ -1466,6 +1468,7 @@ static int clk_osm_resources_init(struct platform_device *pdev)
return -ENOMEM;
}
perfcl_clk.pbases[ACD_BASE] = pbase;
+ perfcl_clk.acd_debugfs_addr_size = resource_size(res);
perfcl_clk.vbases[ACD_BASE] = vbase;
perfcl_clk.acd_init = true;
} else {
@@ -3015,6 +3018,11 @@ static int debugfs_get_debug_reg(void *data, u64 *val)
{
struct clk_osm *c = data;
+ if (!c->pbases[ACD_BASE]) {
+ pr_err("ACD base start not defined\n");
+ return -EINVAL;
+ }
+
if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
*val = readl_relaxed((char *)c->vbases[ACD_BASE] +
c->acd_debugfs_addr);
@@ -3027,6 +3035,11 @@ static int debugfs_set_debug_reg(void *data, u64 val)
{
struct clk_osm *c = data;
+ if (!c->pbases[ACD_BASE]) {
+ pr_err("ACD base start not defined\n");
+ return -EINVAL;
+ }
+
if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
clk_osm_acd_master_write_reg(c, val, c->acd_debugfs_addr);
else
@@ -3044,7 +3057,13 @@ static int debugfs_get_debug_reg_addr(void *data, u64 *val)
{
struct clk_osm *c = data;
+ if (!c->pbases[ACD_BASE]) {
+ pr_err("ACD base start not defined\n");
+ return -EINVAL;
+ }
+
*val = c->acd_debugfs_addr;
+
return 0;
}
@@ -3052,7 +3071,16 @@ static int debugfs_set_debug_reg_addr(void *data, u64 val)
{
struct clk_osm *c = data;
+ if (!c->pbases[ACD_BASE]) {
+ pr_err("ACD base start not defined\n");
+ return -EINVAL;
+ }
+
+ if (val >= c->acd_debugfs_addr_size)
+ return -EINVAL;
+
c->acd_debugfs_addr = val;
+
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(debugfs_acd_debug_reg_addr_fops,
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index d99e13817a29..510a9803bd82 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -226,8 +226,8 @@ enum clk_osm_trace_packet_id {
#define PLL_DD_D0_USER_CTL_LO 0x17916208
#define PLL_DD_D1_USER_CTL_LO 0x17816208
-#define PWRCL_EFUSE_SHIFT 0
-#define PWRCL_EFUSE_MASK 0
+#define PWRCL_EFUSE_SHIFT 29
+#define PWRCL_EFUSE_MASK 0x7
#define PERFCL_EFUSE_SHIFT 29
#define PERFCL_EFUSE_MASK 0x7
@@ -384,6 +384,7 @@ struct clk_osm {
u32 acd_extint1_cfg;
u32 acd_autoxfer_ctl;
u32 acd_debugfs_addr;
+ u32 acd_debugfs_addr_size;
bool acd_init;
bool secure_init;
bool red_fsm_en;
@@ -622,18 +623,21 @@ static inline bool is_better_rate(unsigned long req, unsigned long best,
return (req <= new && new < best) || (best < req && best < new);
}
-static long clk_osm_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_osm_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i;
unsigned long rrate = 0;
+ unsigned long rate = req->rate;
/*
* If the rate passed in is 0, return the first frequency in the
* FMAX table.
*/
- if (!rate)
- return hw->init->rate_max[0];
+ if (!rate) {
+ req->rate = hw->init->rate_max[0];
+ return 0;
+ }
for (i = 0; i < hw->init->num_rate_max; i++) {
if (is_better_rate(rate, rrate, hw->init->rate_max[i])) {
@@ -643,10 +647,12 @@ static long clk_osm_round_rate(struct clk_hw *hw, unsigned long rate,
}
}
+ req->rate = rrate;
+
pr_debug("%s: rate %lu, rrate %ld, Rate max %ld\n", __func__, rate,
rrate, hw->init->rate_max[i]);
- return rrate;
+ return 0;
}
static int clk_osm_search_table(struct osm_entry *table, int entries, long rate)
@@ -677,18 +683,19 @@ static int clk_osm_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_osm *cpuclk = to_clk_osm(hw);
int index = 0;
- unsigned long r_rate;
+ struct clk_rate_request req;
- r_rate = clk_osm_round_rate(hw, rate, NULL);
+ req.rate = rate;
+ clk_osm_determine_rate(hw, &req);
- if (rate != r_rate) {
+ if (rate != req.rate) {
pr_err("invalid rate requested rate=%ld\n", rate);
return -EINVAL;
}
/* Convert rate to table index */
index = clk_osm_search_table(cpuclk->osm_table,
- cpuclk->num_entries, r_rate);
+ cpuclk->num_entries, req.rate);
if (index < 0) {
pr_err("cannot set cluster %u to %lu\n",
cpuclk->cluster_num, rate);
@@ -772,7 +779,7 @@ static unsigned long clk_osm_recalc_rate(struct clk_hw *hw,
static struct clk_ops clk_ops_cpu_osm = {
.enable = clk_osm_enable,
.set_rate = clk_osm_set_rate,
- .round_rate = clk_osm_round_rate,
+ .determine_rate = clk_osm_determine_rate,
.list_rate = clk_osm_list_rate,
.recalc_rate = clk_osm_recalc_rate,
.debug_init = clk_debug_measure_add,
@@ -1371,6 +1378,7 @@ static int clk_osm_resources_init(struct platform_device *pdev)
return -ENOMEM;
}
pwrcl_clk.pbases[ACD_BASE] = pbase;
+ pwrcl_clk.acd_debugfs_addr_size = resource_size(res);
pwrcl_clk.vbases[ACD_BASE] = vbase;
pwrcl_clk.acd_init = true;
} else {
@@ -1388,6 +1396,7 @@ static int clk_osm_resources_init(struct platform_device *pdev)
return -ENOMEM;
}
perfcl_clk.pbases[ACD_BASE] = pbase;
+ perfcl_clk.acd_debugfs_addr_size = resource_size(res);
perfcl_clk.vbases[ACD_BASE] = vbase;
perfcl_clk.acd_init = true;
} else {
@@ -2832,6 +2841,11 @@ static int debugfs_get_debug_reg(void *data, u64 *val)
{
struct clk_osm *c = data;
+ if (!c->pbases[ACD_BASE]) {
+ pr_err("ACD base start not defined\n");
+ return -EINVAL;
+ }
+
if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
*val = readl_relaxed((char *)c->vbases[ACD_BASE] +
c->acd_debugfs_addr);
@@ -2844,6 +2858,11 @@ static int debugfs_set_debug_reg(void *data, u64 val)
{
struct clk_osm *c = data;
+ if (!c->pbases[ACD_BASE]) {
+ pr_err("ACD base start not defined\n");
+ return -EINVAL;
+ }
+
if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
clk_osm_acd_master_write_reg(c, val, c->acd_debugfs_addr);
else
@@ -2861,7 +2880,13 @@ static int debugfs_get_debug_reg_addr(void *data, u64 *val)
{
struct clk_osm *c = data;
+ if (!c->pbases[ACD_BASE]) {
+ pr_err("ACD base start not defined\n");
+ return -EINVAL;
+ }
+
*val = c->acd_debugfs_addr;
+
return 0;
}
@@ -2869,6 +2894,14 @@ static int debugfs_set_debug_reg_addr(void *data, u64 val)
{
struct clk_osm *c = data;
+ if (!c->pbases[ACD_BASE]) {
+ pr_err("ACD base start not defined\n");
+ return -EINVAL;
+ }
+
+ if (val >= c->acd_debugfs_addr_size)
+ return -EINVAL;
+
c->acd_debugfs_addr = val;
return 0;
}
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index b91e115462ae..abbee61c99c8 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -479,6 +479,7 @@ static void cpufreq_interactive_timer(unsigned long data)
bool skip_hispeed_logic, skip_min_sample_time;
bool jump_to_max_no_ts = false;
bool jump_to_max = false;
+ bool start_hyst = true;
if (!down_read_trylock(&ppol->enable_sem))
return;
@@ -588,8 +589,12 @@ static void cpufreq_interactive_timer(unsigned long data)
}
if (now - ppol->max_freq_hyst_start_time <
- tunables->max_freq_hysteresis)
+ tunables->max_freq_hysteresis) {
+ if (new_freq < ppol->policy->max &&
+ ppol->policy->max <= tunables->hispeed_freq)
+ start_hyst = false;
new_freq = max(tunables->hispeed_freq, new_freq);
+ }
if (!skip_hispeed_logic &&
ppol->target_freq >= tunables->hispeed_freq &&
@@ -646,7 +651,7 @@ static void cpufreq_interactive_timer(unsigned long data)
ppol->floor_validate_time = now;
}
- if (new_freq >= ppol->policy->max && !jump_to_max_no_ts)
+ if (start_hyst && new_freq >= ppol->policy->max && !jump_to_max_no_ts)
ppol->max_freq_hyst_start_time = now;
if (ppol->target_freq == new_freq &&
diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c
index 0dadb6332f0e..7abe908427df 100644
--- a/drivers/crypto/atmel-sha.c
+++ b/drivers/crypto/atmel-sha.c
@@ -963,7 +963,9 @@ static int atmel_sha_finup(struct ahash_request *req)
ctx->flags |= SHA_FLAGS_FINUP;
err1 = atmel_sha_update(req);
- if (err1 == -EINPROGRESS || err1 == -EBUSY)
+ if (err1 == -EINPROGRESS ||
+ (err1 == -EBUSY && (ahash_request_flags(req) &
+ CRYPTO_TFM_REQ_MAY_BACKLOG)))
return err1;
/*
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index 99d5e11db194..e06cc5df30be 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -498,7 +498,7 @@ static int hash_digest_key(struct caam_hash_ctx *ctx, const u8 *key_in,
ret = caam_jr_enqueue(jrdev, desc, split_key_done, &result);
if (!ret) {
/* in progress */
- wait_for_completion_interruptible(&result.completion);
+ wait_for_completion(&result.completion);
ret = result.err;
#ifdef DEBUG
print_hex_dump(KERN_ERR,
diff --git a/drivers/crypto/caam/key_gen.c b/drivers/crypto/caam/key_gen.c
index e1eaf4ff9762..3ce1d5cdcbd2 100644
--- a/drivers/crypto/caam/key_gen.c
+++ b/drivers/crypto/caam/key_gen.c
@@ -103,7 +103,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
ret = caam_jr_enqueue(jrdev, desc, split_key_done, &result);
if (!ret) {
/* in progress */
- wait_for_completion_interruptible(&result.completion);
+ wait_for_completion(&result.completion);
ret = result.err;
#ifdef DEBUG
print_hex_dump(KERN_ERR, "ctx.key@"__stringify(__LINE__)": ",
diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c
index 49165daa807f..490f8d9ddb9f 100644
--- a/drivers/crypto/msm/ice.c
+++ b/drivers/crypto/msm/ice.c
@@ -975,7 +975,8 @@ static int qcom_ice_secure_ice_init(struct ice_device *ice_dev)
static int qcom_ice_update_sec_cfg(struct ice_device *ice_dev)
{
- int ret = 0, scm_ret = 0;
+ int ret = 0;
+ u64 scm_ret = 0;
/* scm command buffer structure */
struct qcom_scm_cmd_buf {
@@ -1001,7 +1002,7 @@ static int qcom_ice_update_sec_cfg(struct ice_device *ice_dev)
cbuf.device_id = ICE_TZ_DEV_ID;
ret = scm_restore_sec_cfg(cbuf.device_id, cbuf.spare, &scm_ret);
if (ret || scm_ret) {
- pr_err("%s: failed, ret %d scm_ret %d\n",
+ pr_err("%s: failed, ret %d scm_ret %llu\n",
__func__, ret, scm_ret);
if (!ret)
ret = scm_ret;
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 4ab8ca143f6c..b44f926a6ba0 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -2155,6 +2155,10 @@ static int _sha_complete(struct qce_device *pce_dev, int req_info)
pce_sps_data = &preq_info->ce_sps;
qce_callback = preq_info->qce_cb;
areq = (struct ahash_request *) preq_info->areq;
+ if (!areq) {
+ pr_err("sha operation error. areq is NULL\n");
+ return -ENXIO;
+ }
qce_dma_unmap_sg(pce_dev->pdev, areq->src, preq_info->src_nents,
DMA_TO_DEVICE);
memcpy(digest, (char *)(&pce_sps_data->result->auth_iv[0]),
@@ -2970,7 +2974,7 @@ static inline int qce_alloc_req_info(struct qce_device *pce_dev)
request_index++;
if (request_index >= MAX_QCE_BAM_REQ)
request_index = 0;
- if (xchg(&pce_dev->ce_request_info[request_index].
+ if (atomic_xchg(&pce_dev->ce_request_info[request_index].
in_use, true) == false) {
pce_dev->ce_request_index = request_index;
return request_index;
@@ -2986,7 +2990,8 @@ static inline void qce_free_req_info(struct qce_device *pce_dev, int req_info,
bool is_complete)
{
pce_dev->ce_request_info[req_info].xfer_type = QCE_XFER_TYPE_LAST;
- if (xchg(&pce_dev->ce_request_info[req_info].in_use, false) == true) {
+ if (atomic_xchg(&pce_dev->ce_request_info[req_info].in_use,
+ false) == true) {
if (req_info < MAX_QCE_BAM_REQ && is_complete)
atomic_dec(&pce_dev->no_of_queued_req);
} else
@@ -4610,7 +4615,7 @@ static int qce_dummy_req(struct qce_device *pce_dev)
{
int ret = 0;
- if (!(xchg(&pce_dev->ce_request_info[DUMMY_REQ_INDEX].
+ if (!(atomic_xchg(&pce_dev->ce_request_info[DUMMY_REQ_INDEX].
in_use, true) == false))
return -EBUSY;
ret = qce_process_sha_req(pce_dev, NULL);
@@ -5969,7 +5974,7 @@ void *qce_open(struct platform_device *pdev, int *rc)
}
for (i = 0; i < MAX_QCE_ALLOC_BAM_REQ; i++)
- pce_dev->ce_request_info[i].in_use = false;
+ atomic_set(&pce_dev->ce_request_info[i].in_use, false);
pce_dev->ce_request_index = 0;
pce_dev->memsize = 10 * PAGE_SIZE * MAX_QCE_ALLOC_BAM_REQ;
@@ -6133,12 +6138,13 @@ EXPORT_SYMBOL(qce_hw_support);
void qce_dump_req(void *handle)
{
int i;
+ bool req_in_use;
struct qce_device *pce_dev = (struct qce_device *)handle;
for (i = 0; i < MAX_QCE_BAM_REQ; i++) {
- pr_info("qce_dump_req %d %d\n", i,
- pce_dev->ce_request_info[i].in_use);
- if (pce_dev->ce_request_info[i].in_use == true)
+ req_in_use = atomic_read(&pce_dev->ce_request_info[i].in_use);
+ pr_info("qce_dump_req %d %d\n", i, req_in_use);
+ if (req_in_use == true)
_qce_dump_descr_fifos(pce_dev, i);
}
}
diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h
index 6dba3664ff08..ab0d21da72c5 100644
--- a/drivers/crypto/msm/qce50.h
+++ b/drivers/crypto/msm/qce50.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -214,7 +214,7 @@ struct ce_sps_data {
};
struct ce_request_info {
- bool in_use;
+ atomic_t in_use;
bool in_prog;
enum qce_xfer_type_enum xfer_type;
struct ce_sps_data ce_sps;
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 5b364f053b1b..f38fc422b35e 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -3972,6 +3972,7 @@ static int _sha1_hmac_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int len)
{
struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(&tfm->base);
+ int ret = 0;
memset(&sha_ctx->authkey[0], 0, SHA1_BLOCK_SIZE);
if (len <= SHA1_BLOCK_SIZE) {
memcpy(&sha_ctx->authkey[0], key, len);
@@ -3979,16 +3980,19 @@ static int _sha1_hmac_setkey(struct crypto_ahash *tfm, const u8 *key,
} else {
sha_ctx->alg = QCE_HASH_SHA1;
sha_ctx->diglen = SHA1_DIGEST_SIZE;
- _sha_hmac_setkey(tfm, key, len);
+ ret = _sha_hmac_setkey(tfm, key, len);
+ if (ret)
+ pr_err("SHA1 hmac setkey failed\n");
sha_ctx->authkey_in_len = SHA1_BLOCK_SIZE;
}
- return 0;
+ return ret;
}
static int _sha256_hmac_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int len)
{
struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(&tfm->base);
+ int ret = 0;
memset(&sha_ctx->authkey[0], 0, SHA256_BLOCK_SIZE);
if (len <= SHA256_BLOCK_SIZE) {
@@ -3997,11 +4001,13 @@ static int _sha256_hmac_setkey(struct crypto_ahash *tfm, const u8 *key,
} else {
sha_ctx->alg = QCE_HASH_SHA256;
sha_ctx->diglen = SHA256_DIGEST_SIZE;
- _sha_hmac_setkey(tfm, key, len);
+ ret = _sha_hmac_setkey(tfm, key, len);
+ if (ret)
+ pr_err("SHA256 hmac setkey failed\n");
sha_ctx->authkey_in_len = SHA256_BLOCK_SIZE;
}
- return 0;
+ return ret;
}
static int _sha_hmac_init_ihash(struct ahash_request *req,
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 9a8a18aafd5c..6a60936b46e0 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -804,7 +804,7 @@ static void talitos_unregister_rng(struct device *dev)
* crypto alg
*/
#define TALITOS_CRA_PRIORITY 3000
-#define TALITOS_MAX_KEY_SIZE 96
+#define TALITOS_MAX_KEY_SIZE (AES_MAX_KEY_SIZE + SHA512_BLOCK_SIZE)
#define TALITOS_MAX_IV_LENGTH 16 /* max of AES_BLOCK_SIZE, DES3_EDE_BLOCK_SIZE */
struct talitos_ctx {
@@ -1388,6 +1388,11 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *cipher,
{
struct talitos_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ if (keylen > TALITOS_MAX_KEY_SIZE) {
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
memcpy(&ctx->key, key, keylen);
ctx->keylen = keylen;
diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h
index 690e3b4f8202..b36da3c1073f 100644
--- a/drivers/dma/ioat/hw.h
+++ b/drivers/dma/ioat/hw.h
@@ -64,6 +64,8 @@
#define PCI_DEVICE_ID_INTEL_IOAT_BDX8 0x6f2e
#define PCI_DEVICE_ID_INTEL_IOAT_BDX9 0x6f2f
+#define PCI_DEVICE_ID_INTEL_IOAT_SKX 0x2021
+
#define IOAT_VER_1_2 0x12 /* Version 1.2 */
#define IOAT_VER_2_0 0x20 /* Version 2.0 */
#define IOAT_VER_3_0 0x30 /* Version 3.0 */
diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c
index 4ef0c5e07912..abb75ebd65ea 100644
--- a/drivers/dma/ioat/init.c
+++ b/drivers/dma/ioat/init.c
@@ -105,6 +105,8 @@ static struct pci_device_id ioat_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX8) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX9) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SKX) },
+
/* I/OAT v3.3 platforms */
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD0) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD1) },
@@ -250,10 +252,15 @@ static bool is_bdx_ioat(struct pci_dev *pdev)
}
}
+static inline bool is_skx_ioat(struct pci_dev *pdev)
+{
+ return (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_SKX) ? true : false;
+}
+
static bool is_xeon_cb32(struct pci_dev *pdev)
{
return is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev) ||
- is_hsw_ioat(pdev) || is_bdx_ioat(pdev);
+ is_hsw_ioat(pdev) || is_bdx_ioat(pdev) || is_skx_ioat(pdev);
}
bool is_bwd_ioat(struct pci_dev *pdev)
@@ -1350,6 +1357,8 @@ static int ioat_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
device->version = readb(device->reg_base + IOAT_VER_OFFSET);
if (device->version >= IOAT_VER_3_0) {
+ if (is_skx_ioat(pdev))
+ device->version = IOAT_VER_3_2;
err = ioat3_dma_probe(device, ioat_dca_enabled);
if (device->version >= IOAT_VER_3_3)
diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
index a415edbe61b1..149ec2bd9bc6 100644
--- a/drivers/dma/ti-dma-crossbar.c
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -146,6 +146,7 @@ static int ti_am335x_xbar_probe(struct platform_device *pdev)
match = of_match_node(ti_am335x_master_match, dma_node);
if (!match) {
dev_err(&pdev->dev, "DMA master is not supported\n");
+ of_node_put(dma_node);
return -EINVAL;
}
@@ -310,6 +311,7 @@ static int ti_dra7_xbar_probe(struct platform_device *pdev)
match = of_match_node(ti_dra7_master_match, dma_node);
if (!match) {
dev_err(&pdev->dev, "DMA master is not supported\n");
+ of_node_put(dma_node);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 475c38fe9245..e40a6d8b0b92 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -1126,6 +1126,9 @@ static ssize_t amdgpu_ttm_vram_read(struct file *f, char __user *buf,
if (size & 0x3 || *pos & 0x3)
return -EINVAL;
+ if (*pos >= adev->mc.mc_vram_size)
+ return -ENXIO;
+
while (size) {
unsigned long flags;
uint32_t value;
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 6253775b8d9c..50d74e5ce41b 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1247,6 +1247,9 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
if (config->funcs->atomic_check)
ret = config->funcs->atomic_check(state->dev, state);
+ if (ret)
+ return ret;
+
if (!state->allow_modeset) {
for_each_crtc_in_state(state, crtc, crtc_state, i) {
if (drm_atomic_crtc_needs_modeset(crtc_state)) {
@@ -1257,7 +1260,7 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
}
}
- return ret;
+ return 0;
}
EXPORT_SYMBOL(drm_atomic_check_only);
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index a3b96d691ac9..58bf94b69186 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -330,6 +330,13 @@ static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg,
return false;
}
+ /*
+ * ignore out-of-order messages or messages that are part of a
+ * failed transaction
+ */
+ if (!recv_hdr.somt && !msg->have_somt)
+ return false;
+
/* get length contained in this portion */
msg->curchunk_len = recv_hdr.msg_len;
msg->curchunk_hdrlen = hdrlen;
@@ -2163,7 +2170,7 @@ out_unlock:
}
EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
-static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
+static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
{
int len;
u8 replyblock[32];
@@ -2178,12 +2185,12 @@ static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
replyblock, len);
if (ret != len) {
DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret);
- return;
+ return false;
}
ret = drm_dp_sideband_msg_build(msg, replyblock, len, true);
if (!ret) {
DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]);
- return;
+ return false;
}
replylen = msg->curchunk_len + msg->curchunk_hdrlen;
@@ -2195,21 +2202,32 @@ static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply,
replyblock, len);
if (ret != len) {
- DRM_DEBUG_KMS("failed to read a chunk\n");
+ DRM_DEBUG_KMS("failed to read a chunk (len %d, ret %d)\n",
+ len, ret);
+ return false;
}
+
ret = drm_dp_sideband_msg_build(msg, replyblock, len, false);
- if (ret == false)
+ if (!ret) {
DRM_DEBUG_KMS("failed to build sideband msg\n");
+ return false;
+ }
+
curreply += len;
replylen -= len;
}
+ return true;
}
static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
{
int ret = 0;
- drm_dp_get_one_sb_msg(mgr, false);
+ if (!drm_dp_get_one_sb_msg(mgr, false)) {
+ memset(&mgr->down_rep_recv, 0,
+ sizeof(struct drm_dp_sideband_msg_rx));
+ return 0;
+ }
if (mgr->down_rep_recv.have_eomt) {
struct drm_dp_sideband_msg_tx *txmsg;
@@ -2265,7 +2283,12 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
{
int ret = 0;
- drm_dp_get_one_sb_msg(mgr, true);
+
+ if (!drm_dp_get_one_sb_msg(mgr, true)) {
+ memset(&mgr->up_req_recv, 0,
+ sizeof(struct drm_dp_sideband_msg_rx));
+ return 0;
+ }
if (mgr->up_req_recv.have_eomt) {
struct drm_dp_sideband_msg_req_body msg;
@@ -2317,7 +2340,9 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
}
- drm_dp_put_mst_branch_device(mstb);
+ if (mstb)
+ drm_dp_put_mst_branch_device(mstb);
+
memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
}
return ret;
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 39b8e171cad5..47c1747e7ae3 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -2716,6 +2716,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
#define VENDOR_BLOCK 0x03
#define SPEAKER_BLOCK 0x04
#define HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK 0x06
+#define COLORIMETRY_EXTENDED_DATA_BLOCK 0x05
#define EXTENDED_TAG 0x07
#define VIDEO_CAPABILITY_BLOCK 0x07
#define Y420_VIDEO_DATA_BLOCK 0x0E
@@ -3526,11 +3527,17 @@ drm_extract_vcdb_info(struct drm_connector *connector, const u8 *db)
(db[2] & (BIT(3) | BIT(2))) >> 2;
connector->ce_scan_info =
db[2] & (BIT(1) | BIT(0));
+ connector->rgb_qs =
+ db[2] & BIT(6);
+ connector->yuv_qs =
+ db[2] & BIT(7);
DRM_DEBUG_KMS("Scan Info (pt|it|ce): (%d|%d|%d)",
(int) connector->pt_scan_info,
(int) connector->it_scan_info,
(int) connector->ce_scan_info);
+ DRM_DEBUG_KMS("rgb_quant_range_select %d", connector->rgb_qs);
+ DRM_DEBUG_KMS("ycc_quant_range_select %d", connector->yuv_qs);
}
static bool drm_edid_is_luminance_value_present(
@@ -3589,6 +3596,50 @@ drm_extract_hdr_db(struct drm_connector *connector, const u8 *db)
}
/*
+ * drm_extract_colorimetry_db - Parse the HDMI colorimetry extended block
+ * @connector: connector corresponding to the HDMI sink
+ * @db: start of the HDMI colorimetry extended block
+ *
+ * Parses the HDMI colorimetry block to extract sink info for @connector.
+ */
+static void
+drm_extract_clrmetry_db(struct drm_connector *connector, const u8 *db)
+{
+
+ if (!db) {
+ DRM_ERROR("invalid db\n");
+ return;
+ }
+
+ /* Bit 0: xvYCC_601 */
+ if (db[2] & BIT(0))
+ connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_xvYCC_601;
+ /* Bit 0: xvYCC_709 */
+ if (db[2] & BIT(1))
+ connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_xvYCC_709;
+ /* Bit 0: sYCC_601 */
+ if (db[2] & BIT(2))
+ connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_sYCC_601;
+ /* Bit 0: ADBYCC_601 */
+ if (db[2] & BIT(3))
+ connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_ADBYCC_601;
+ /* Bit 0: ADB_RGB */
+ if (db[2] & BIT(4))
+ connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_ADB_RGB;
+ /* Bit 0: BT2020_CYCC */
+ if (db[2] & BIT(5))
+ connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_BT2020_CYCC;
+ /* Bit 0: BT2020_YCC */
+ if (db[2] & BIT(6))
+ connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_BT2020_YCC;
+ /* Bit 0: BT2020_RGB */
+ if (db[2] & BIT(7))
+ connector->color_enc_fmt |= DRM_EDID_COLORIMETRY_BT2020_RGB;
+
+ DRM_DEBUG_KMS("colorimetry fmt 0x%x\n", connector->color_enc_fmt);
+}
+
+/*
* drm_hdmi_extract_extended_blk_info - Parse the HDMI extended tag blocks
* @connector: connector corresponding to the HDMI sink
* @edid: handle to the EDID structure
@@ -3620,6 +3671,9 @@ struct edid *edid)
case HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK:
drm_extract_hdr_db(connector, db);
break;
+ case COLORIMETRY_EXTENDED_DATA_BLOCK:
+ drm_extract_clrmetry_db(connector, db);
+ break;
default:
break;
}
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index b205224f1a44..9147113139be 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -715,13 +715,13 @@ drm_gem_object_release_handle(int id, void *ptr, void *data)
struct drm_gem_object *obj = ptr;
struct drm_device *dev = obj->dev;
+ if (dev->driver->gem_close_object)
+ dev->driver->gem_close_object(obj, file_priv);
+
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_gem_remove_prime_handles(obj, file_priv);
drm_vma_node_revoke(&obj->vma_node, file_priv->filp);
- if (dev->driver->gem_close_object)
- dev->driver->gem_close_object(obj, file_priv);
-
drm_gem_object_handle_unreference_unlocked(obj);
return 0;
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index dbc198b00792..cb3b25ddd0da 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -98,3 +98,13 @@ config DRM_SDE_HDMI
default y
help
Choose this option if HDMI connector support is needed in SDE driver.
+
+config DRM_SDE_EVTLOG_DEBUG
+ bool "Enable event logging in MSM DRM"
+ depends on DRM_MSM
+ help
+ The SDE DRM debugging provides support to enable display debugging
+ features to: dump SDE registers during driver errors, panic
+ driver during fatal errors and enable some display-driver logging
+ into an internal buffer (this avoids logging overhead).
+
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 84125b3d1f95..678b2178cb69 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -48,6 +48,8 @@ msm_drm-y := \
sde/sde_backlight.o \
sde/sde_color_processing.o \
sde/sde_vbif.o \
+ sde/sde_splash.o \
+ sde_dbg.o \
sde_dbg_evtlog.o \
sde_io_util.o \
dba_bridge.o \
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index 45a38b247727..765c1c087c76 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -15,6 +15,7 @@
#include "msm_iommu.h"
#include "msm_trace.h"
#include "a5xx_gpu.h"
+#include <linux/clk/msm-clk.h>
#define SECURE_VA_START 0xc0000000
#define SECURE_VA_SIZE SZ_256M
@@ -1169,6 +1170,17 @@ static int a5xx_pm_resume(struct msm_gpu *gpu)
{
int ret;
+ /*
+ * Between suspend/resumes the GPU clocks need to be turned off
+ * but not a complete power down, typically between frames. Set the
+ * memory retention flags on the GPU core clock to retain memory
+ * across clock toggles.
+ */
+ if (gpu->core_clk) {
+ clk_set_flags(gpu->core_clk, CLKFLAG_RETAIN_PERIPH);
+ clk_set_flags(gpu->core_clk, CLKFLAG_RETAIN_MEM);
+ }
+
/* Turn on the core power */
ret = msm_gpu_pm_resume(gpu);
if (ret)
@@ -1208,6 +1220,12 @@ static int a5xx_pm_suspend(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ /* Turn off the memory retention flag when not necessary */
+ if (gpu->core_clk) {
+ clk_set_flags(gpu->core_clk, CLKFLAG_NORETAIN_PERIPH);
+ clk_set_flags(gpu->core_clk, CLKFLAG_NORETAIN_MEM);
+ }
+
/* Only do this next bit if we are about to go down */
if (gpu->active_cnt == 1) {
/* Clear the VBIF pipe before shutting down */
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
index e637237fa811..c30b65785ab6 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
@@ -70,6 +70,8 @@ struct a5xx_gpu {
* PREEMPT_NONE - no preemption in progress. Next state START.
* PREEMPT_START - The trigger is evaulating if preemption is possible. Next
* states: TRIGGERED, NONE
+ * PREEMPT_ABORT - An intermediate state before moving back to NONE. Next
+ * state: NONE.
* PREEMPT_TRIGGERED: A preemption has been executed on the hardware. Next
* states: FAULTED, PENDING
* PREEMPT_FAULTED: A preemption timed out (never completed). This will trigger
@@ -81,6 +83,7 @@ struct a5xx_gpu {
enum preempt_state {
PREEMPT_NONE = 0,
PREEMPT_START,
+ PREEMPT_ABORT,
PREEMPT_TRIGGERED,
PREEMPT_FAULTED,
PREEMPT_PENDING,
@@ -184,7 +187,10 @@ int a5xx_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot);
/* Return true if we are in a preempt state */
static inline bool a5xx_in_preempt(struct a5xx_gpu *a5xx_gpu)
{
- return !(atomic_read(&a5xx_gpu->preempt_state) == PREEMPT_NONE);
+ int preempt_state = atomic_read(&a5xx_gpu->preempt_state);
+
+ return !(preempt_state == PREEMPT_NONE ||
+ preempt_state == PREEMPT_ABORT);
}
int a5xx_counters_init(struct adreno_gpu *adreno_gpu);
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c
index 6ab3ba076c2f..44d4ca35fa09 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c
@@ -128,9 +128,20 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu)
* one do nothing except to update the wptr to the latest and greatest
*/
if (!ring || (a5xx_gpu->cur_ring == ring)) {
- update_wptr(gpu, ring);
-
- /* Set the state back to NONE */
+ /*
+ * Its possible that while a preemption request is in progress
+ * from an irq context, a user context trying to submit might
+ * fail to update the write pointer, because it determines
+ * that the preempt state is not PREEMPT_NONE.
+ *
+ * Close the race by introducing an intermediate
+ * state PREEMPT_ABORT to let the submit path
+ * know that the ringbuffer is not going to change
+ * and can safely update the write pointer.
+ */
+
+ set_preempt_state(a5xx_gpu, PREEMPT_ABORT);
+ update_wptr(gpu, a5xx_gpu->cur_ring);
set_preempt_state(a5xx_gpu, PREEMPT_NONE);
return;
}
diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c
index a498a60cd52d..4e4709d6172f 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_device.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_device.c
@@ -173,6 +173,9 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev)
ret = gpu->funcs->hw_init(gpu);
if (ret) {
dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
+ mutex_lock(&dev->struct_mutex);
+ gpu->funcs->pm_suspend(gpu);
+ mutex_unlock(&dev->struct_mutex);
gpu->funcs->destroy(gpu);
gpu = NULL;
} else {
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index e8b3e85603e4..c98f4511d644 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -1253,6 +1253,13 @@ static int _sde_hdmi_hpd_enable(struct sde_hdmi *sde_hdmi)
uint32_t hpd_ctrl;
int i, ret;
unsigned long flags;
+ struct drm_connector *connector;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
+
+ connector = hdmi->connector;
+ priv = connector->dev->dev_private;
+ sde_kms = to_sde_kms(priv->kms);
for (i = 0; i < config->hpd_reg_cnt; i++) {
ret = regulator_enable(hdmi->hpd_regs[i]);
@@ -1292,9 +1299,11 @@ static int _sde_hdmi_hpd_enable(struct sde_hdmi *sde_hdmi)
}
}
- sde_hdmi_set_mode(hdmi, false);
- _sde_hdmi_phy_reset(hdmi);
- sde_hdmi_set_mode(hdmi, true);
+ if (!sde_kms->splash_info.handoff) {
+ sde_hdmi_set_mode(hdmi, false);
+ _sde_hdmi_phy_reset(hdmi);
+ sde_hdmi_set_mode(hdmi, true);
+ }
hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
@@ -1961,6 +1970,123 @@ enable_packet_control:
hdmi_write(hdmi, HDMI_GEN_PKT_CTRL, packet_control);
}
+static void sde_hdmi_update_colorimetry(struct sde_hdmi *display,
+ bool use_bt2020)
+{
+ struct hdmi *hdmi;
+ struct drm_connector *connector;
+ bool mode_is_yuv = false;
+ struct drm_display_mode *mode;
+ u32 mode_fmt_flags = 0;
+ u8 checksum;
+ u32 avi_info0 = 0;
+ u32 avi_info1 = 0;
+ u8 avi_iframe[HDMI_AVI_INFOFRAME_BUFFER_SIZE] = {0};
+ u8 *avi_frame = &avi_iframe[HDMI_INFOFRAME_HEADER_SIZE];
+ struct hdmi_avi_infoframe info;
+
+ if (!display) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ connector = display->ctrl.ctrl->connector;
+
+ if (!connector) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ if (!connector->hdr_supported) {
+ SDE_DEBUG("HDR is not supported\n");
+ return;
+ }
+
+ /* If sink doesn't support BT2020, just return */
+ if (!(connector->color_enc_fmt & DRM_EDID_COLORIMETRY_BT2020_YCC) ||
+ !(connector->color_enc_fmt & DRM_EDID_COLORIMETRY_BT2020_RGB)) {
+ SDE_DEBUG("BT2020 colorimetry is not supported\n");
+ return;
+ }
+
+ /* If there is no change in colorimetry, just return */
+ if (use_bt2020 && display->bt2020_colorimetry)
+ return;
+ else if (!use_bt2020 && !display->bt2020_colorimetry)
+ return;
+
+ mode = &display->mode;
+ /* Cache the format flags before clearing */
+ mode_fmt_flags = mode->flags;
+ /**
+ * Clear the RGB/YUV format flags before calling upstream API
+ * as the API also compares the flags and then returns a mode
+ */
+ mode->flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK;
+ drm_hdmi_avi_infoframe_from_display_mode(&info, mode);
+ /* Restore the format flags */
+ mode->flags = mode_fmt_flags;
+
+ /* Mode should only support YUV and not both to set the flag */
+ if ((mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
+ && !(mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_RGB444)) {
+ mode_is_yuv = true;
+ }
+
+
+ if (!display->bt2020_colorimetry && use_bt2020) {
+ /**
+ * 1. Update colorimetry to use extended
+ * 2. Change extended to use BT2020
+ * 3. Change colorspace based on mode
+ * 4. Use limited as BT2020 is always limited
+ */
+ info.colorimetry = SDE_HDMI_USE_EXTENDED_COLORIMETRY;
+ info.extended_colorimetry = SDE_HDMI_BT2020_COLORIMETRY;
+ if (mode_is_yuv)
+ info.colorspace = HDMI_COLORSPACE_YUV420;
+ if (connector->yuv_qs)
+ info.ycc_quantization_range =
+ HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
+ } else if (display->bt2020_colorimetry && !use_bt2020) {
+ /**
+ * 1. Update colorimetry to non-extended
+ * 2. Change colorspace based on mode
+ * 3. Restore quantization to full if QS
+ * is enabled
+ */
+ info.colorimetry = SDE_HDMI_DEFAULT_COLORIMETRY;
+ if (mode_is_yuv)
+ info.colorspace = HDMI_COLORSPACE_YUV420;
+ if (connector->yuv_qs)
+ info.ycc_quantization_range =
+ HDMI_YCC_QUANTIZATION_RANGE_FULL;
+ }
+
+ hdmi_avi_infoframe_pack(&info, avi_iframe, sizeof(avi_iframe));
+ checksum = avi_iframe[HDMI_INFOFRAME_HEADER_SIZE - 1];
+ avi_info0 = checksum |
+ LEFT_SHIFT_BYTE(avi_frame[0]) |
+ LEFT_SHIFT_WORD(avi_frame[1]) |
+ LEFT_SHIFT_24BITS(avi_frame[2]);
+
+ avi_info1 = avi_frame[3] |
+ LEFT_SHIFT_BYTE(avi_frame[4]) |
+ LEFT_SHIFT_WORD(avi_frame[5]) |
+ LEFT_SHIFT_24BITS(avi_frame[6]);
+
+ hdmi_write(hdmi, REG_HDMI_AVI_INFO(0), avi_info0);
+ hdmi_write(hdmi, REG_HDMI_AVI_INFO(1), avi_info1);
+ display->bt2020_colorimetry = use_bt2020;
+}
+
static void sde_hdmi_clear_hdr_infoframe(struct sde_hdmi *display)
{
struct hdmi *hdmi;
@@ -2331,14 +2457,22 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector,
{
struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl;
+ struct drm_msm_ext_panel_hdr_metadata *hdr_meta;
u8 hdr_op;
- if (!connector || !display || !params) {
+ if (!connector || !display || !params ||
+ !params->hdr_ctrl) {
pr_err("Invalid params\n");
return -EINVAL;
}
hdr_ctrl = params->hdr_ctrl;
+ hdr_meta = &hdr_ctrl->hdr_meta;
+
+ if (!hdr_meta) {
+ SDE_ERROR("Invalid params\n");
+ return -EINVAL;
+ }
hdr_op = sde_hdmi_hdr_get_ops(hdmi_display->curr_hdr_state,
hdr_ctrl->hdr_state);
@@ -2347,6 +2481,12 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector,
if (connector->hdr_supported)
sde_hdmi_panel_set_hdr_infoframe(display,
&hdr_ctrl->hdr_meta);
+ if (hdr_meta->eotf)
+ sde_hdmi_update_colorimetry(hdmi_display,
+ true);
+ else
+ sde_hdmi_update_colorimetry(hdmi_display,
+ false);
} else if (hdr_op == HDR_CLEAR_INFO)
sde_hdmi_clear_hdr_infoframe(display);
@@ -2355,6 +2495,70 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector,
return 0;
}
+bool sde_hdmi_mode_needs_full_range(void *display)
+{
+ struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
+ struct drm_display_mode *mode;
+ u32 mode_fmt_flags = 0;
+ u32 cea_mode;
+
+ if (!hdmi_display) {
+ SDE_ERROR("invalid input\n");
+ return false;
+ }
+
+ mode = &hdmi_display->mode;
+ /* Cache the format flags before clearing */
+ mode_fmt_flags = mode->flags;
+ /**
+ * Clear the RGB/YUV format flags before calling upstream API
+ * as the API also compares the flags and then returns a mode
+ */
+ mode->flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK;
+ cea_mode = drm_match_cea_mode(mode);
+ /* Restore the format flags */
+ mode->flags = mode_fmt_flags;
+
+ if (cea_mode > SDE_HDMI_VIC_640x480)
+ return false;
+
+ return true;
+}
+
+enum sde_csc_type sde_hdmi_get_csc_type(struct drm_connector *conn,
+ void *display)
+{
+ struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
+ struct sde_connector_state *c_state;
+ struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl;
+ struct drm_msm_ext_panel_hdr_metadata *hdr_meta;
+
+ if (!hdmi_display || !conn) {
+ SDE_ERROR("invalid input\n");
+ goto error;
+ }
+
+ c_state = to_sde_connector_state(conn->state);
+
+ if (!c_state) {
+ SDE_ERROR("invalid input\n");
+ goto error;
+ }
+
+ hdr_ctrl = &c_state->hdr_ctrl;
+ hdr_meta = &hdr_ctrl->hdr_meta;
+
+ if ((hdr_ctrl->hdr_state == HDR_ENABLE)
+ && (hdr_meta->eotf != 0))
+ return SDE_CSC_RGB2YUV_2020L;
+ else if (sde_hdmi_mode_needs_full_range(hdmi_display)
+ || conn->yuv_qs)
+ return SDE_CSC_RGB2YUV_601FR;
+
+error:
+ return SDE_CSC_RGB2YUV_601L;
+}
+
int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
{
struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
@@ -2863,6 +3067,7 @@ int sde_hdmi_drm_init(struct sde_hdmi *display, struct drm_encoder *enc)
struct msm_drm_private *priv = NULL;
struct hdmi *hdmi;
struct platform_device *pdev;
+ struct sde_kms *sde_kms;
DBG("");
if (!display || !display->drm_dev || !enc) {
@@ -2921,6 +3126,19 @@ int sde_hdmi_drm_init(struct sde_hdmi *display, struct drm_encoder *enc)
enc->bridge = hdmi->bridge;
priv->bridges[priv->num_bridges++] = hdmi->bridge;
+ /*
+ * After initialising HDMI bridge, we need to check
+ * whether the early display is enabled for HDMI.
+ * If yes, we need to increase refcount of hdmi power
+ * clocks. This can skip the clock disabling operation in
+ * clock_late_init when finding clk.count == 1.
+ */
+ sde_kms = to_sde_kms(priv->kms);
+ if (sde_kms->splash_info.handoff) {
+ sde_hdmi_bridge_power_on(hdmi->bridge);
+ hdmi->power_on = true;
+ }
+
mutex_unlock(&display->display_lock);
return 0;
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
index bafb2b949a6b..672a9f188d27 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
@@ -169,6 +169,7 @@ struct sde_hdmi {
bool pll_update_enable;
bool dc_enable;
bool dc_feature_supported;
+ bool bt2020_colorimetry;
struct delayed_work hdcp_cb_work;
struct dss_io_data io[HDMI_TX_MAX_IO];
@@ -201,6 +202,15 @@ enum hdmi_tx_scdc_access_type {
#define HDMI_GEN_PKT_CTRL_CLR_MASK 0x7
+/* for AVI program */
+#define HDMI_AVI_INFOFRAME_BUFFER_SIZE \
+ (HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE)
+#define HDMI_VS_INFOFRAME_BUFFER_SIZE (HDMI_INFOFRAME_HEADER_SIZE + 6)
+
+#define LEFT_SHIFT_BYTE(x) ((x) << 8)
+#define LEFT_SHIFT_WORD(x) ((x) << 16)
+#define LEFT_SHIFT_24BITS(x) ((x) << 24)
+
/* Maximum pixel clock rates for hdmi tx */
#define HDMI_DEFAULT_MAX_PCLK_RATE 148500
#define HDMI_TX_3_MAX_PCLK_RATE 297000
@@ -357,6 +367,13 @@ int sde_hdmi_set_property(struct drm_connector *connector,
int property_index,
uint64_t value,
void *display);
+/**
+ * sde_hdmi_bridge_power_on -- A wrapper of _sde_hdmi_bridge_power_on.
+ * @bridge: Handle to the drm bridge.
+ *
+ * Return: void.
+ */
+void sde_hdmi_bridge_power_on(struct drm_bridge *bridge);
/**
* sde_hdmi_get_property() - get the connector properties
@@ -475,6 +492,23 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector,
void *display,
struct msm_display_kickoff_params *params);
+/*
+ * sde_hdmi_mode_needs_full_range - does mode need full range
+ * quantization
+ * @display: Pointer to private display structure
+ * Returns: true or false based on mode
+ */
+bool sde_hdmi_mode_needs_full_range(void *display);
+
+/*
+ * sde_hdmi_get_csc_type - returns the CSC type to be
+ * used based on state of HDR playback
+ * @conn: Pointer to DRM connector
+ * @display: Pointer to private display structure
+ * Returns: true or false based on mode
+ */
+enum sde_csc_type sde_hdmi_get_csc_type(struct drm_connector *conn,
+ void *display);
#else /*#ifdef CONFIG_DRM_SDE_HDMI*/
static inline u32 sde_hdmi_get_num_of_displays(void)
@@ -589,5 +623,16 @@ static inline int sde_hdmi_set_property(struct drm_connector *connector,
return 0;
}
+static inline bool sde_hdmi_mode_needs_full_range(void *display)
+{
+ return false;
+}
+
+enum sde_csc_type sde_hdmi_get_csc_type(struct drm_connector *conn,
+ void *display)
+{
+ return 0;
+}
+
#endif /*#else of CONFIG_DRM_SDE_HDMI*/
#endif /* _SDE_HDMI_H_ */
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
index 62dd3aaf7078..e6b6d15b5fb7 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
@@ -99,17 +99,10 @@ struct sde_hdmi_bridge {
#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200
-/* for AVI program */
-#define HDMI_AVI_INFOFRAME_BUFFER_SIZE \
- (HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE)
-#define HDMI_VS_INFOFRAME_BUFFER_SIZE (HDMI_INFOFRAME_HEADER_SIZE + 6)
#define HDMI_SPD_INFOFRAME_BUFFER_SIZE \
(HDMI_INFOFRAME_HEADER_SIZE + HDMI_SPD_INFOFRAME_SIZE)
#define HDMI_DEFAULT_VENDOR_NAME "unknown"
#define HDMI_DEFAULT_PRODUCT_NAME "msm"
-#define LEFT_SHIFT_BYTE(x) ((x) << 8)
-#define LEFT_SHIFT_WORD(x) ((x) << 16)
-#define LEFT_SHIFT_24BITS(x) ((x) << 24)
#define HDMI_AVI_IFRAME_LINE_NUMBER 1
#define HDMI_VENDOR_IFRAME_LINE_NUMBER 3
@@ -351,6 +344,7 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
scrambler_on = true;
tmds_clock_ratio = 1;
} else {
+ tmds_clock_ratio = 0;
scrambler_on = connector->supports_scramble;
}
@@ -396,6 +390,14 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
rc = _sde_hdmi_bridge_setup_ddc_timers(hdmi,
HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, timeout_hsync);
} else {
+ /* reset tmds clock ratio */
+ rc = sde_hdmi_scdc_write(hdmi,
+ HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE,
+ tmds_clock_ratio);
+ /* scdc write can fail if sink doesn't support SCDC */
+ if (rc && connector->scdc_present)
+ SDE_ERROR("SCDC present, TMDS clk ratio err\n");
+
sde_hdmi_scdc_write(hdmi, HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0);
reg_val = hdmi_read(hdmi, REG_HDMI_CTRL);
reg_val &= ~BIT(28); /* Unset SCRAMBLER_EN bit */
@@ -573,18 +575,49 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge)
}
static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi,
- const struct drm_display_mode *mode)
+ struct drm_display_mode *mode)
{
u8 avi_iframe[HDMI_AVI_INFOFRAME_BUFFER_SIZE] = {0};
u8 *avi_frame = &avi_iframe[HDMI_INFOFRAME_HEADER_SIZE];
u8 checksum;
u32 reg_val;
+ u32 mode_fmt_flags = 0;
struct hdmi_avi_infoframe info;
+ struct drm_connector *connector;
+
+ if (!hdmi || !mode) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ connector = hdmi->connector;
+
+ if (!connector) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+ /* Cache the format flags before clearing */
+ mode_fmt_flags = mode->flags;
+ /**
+ * Clear the RGB/YUV format flags before calling upstream API
+ * as the API also compares the flags and then returns a mode
+ */
+ mode->flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK;
drm_hdmi_avi_infoframe_from_display_mode(&info, mode);
+ /* Restore the format flags */
+ mode->flags = mode_fmt_flags;
- if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
+ if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) {
info.colorspace = HDMI_COLORSPACE_YUV420;
+ /**
+ * If sink supports quantization select,
+ * override to full range
+ */
+ if (connector->yuv_qs)
+ info.ycc_quantization_range =
+ HDMI_YCC_QUANTIZATION_RANGE_FULL;
+ }
hdmi_avi_infoframe_pack(&info, avi_iframe, sizeof(avi_iframe));
checksum = avi_iframe[HDMI_INFOFRAME_HEADER_SIZE - 1];
@@ -841,6 +874,11 @@ static bool _sde_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
return true;
}
+void sde_hdmi_bridge_power_on(struct drm_bridge *bridge)
+{
+ _sde_hdmi_bridge_power_on(bridge);
+}
+
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
.pre_enable = _sde_hdmi_bridge_pre_enable,
.enable = _sde_hdmi_bridge_enable,
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
index 1d89ae222a7b..3c6b0f1b9dd4 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
@@ -97,6 +97,14 @@
#define HDMI_GET_MSB(x)(x >> 8)
#define HDMI_GET_LSB(x)(x & 0xff)
+#define SDE_HDMI_VIC_640x480 0x1
+#define SDE_HDMI_YCC_QUANT_MASK (0x3 << 14)
+#define SDE_HDMI_COLORIMETRY_MASK (0x3 << 22)
+
+#define SDE_HDMI_DEFAULT_COLORIMETRY 0x0
+#define SDE_HDMI_USE_EXTENDED_COLORIMETRY 0x3
+#define SDE_HDMI_BT2020_COLORIMETRY 0x6
+
/*
* Bits 1:0 in HDMI_HW_DDC_CTRL that dictate how the HDCP 2.2 RxStatus will be
* read by the hardware
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 924ce64206f4..969af4c6f0c0 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -293,7 +293,7 @@ static int msm_unload(struct drm_device *dev)
priv->vram.paddr, &attrs);
}
- sde_evtlog_destroy();
+ sde_dbg_destroy();
sde_power_client_destroy(&priv->phandle, priv->pclient);
sde_power_resource_deinit(pdev, &priv->phandle);
@@ -423,11 +423,17 @@ static int msm_component_bind_all(struct device *dev,
}
#endif
+static int msm_power_enable_wrapper(void *handle, void *client, bool enable)
+{
+ return sde_power_resource_enable(handle, client, enable);
+}
+
static int msm_load(struct drm_device *dev, unsigned long flags)
{
struct platform_device *pdev = dev->platformdev;
struct msm_drm_private *priv;
struct msm_kms *kms;
+ struct sde_dbg_power_ctrl dbg_power_ctrl = { NULL };
int ret, i;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -477,9 +483,13 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
if (ret)
goto fail;
- ret = sde_evtlog_init(dev->primary->debugfs_root);
+ dbg_power_ctrl.handle = &priv->phandle;
+ dbg_power_ctrl.client = priv->pclient;
+ dbg_power_ctrl.enable_fn = msm_power_enable_wrapper;
+ ret = sde_dbg_init(dev->primary->debugfs_root, &pdev->dev,
+ &dbg_power_ctrl);
if (ret) {
- dev_err(dev->dev, "failed to init evtlog: %d\n", ret);
+ dev_err(dev->dev, "failed to init sde dbg: %d\n", ret);
goto fail;
}
@@ -1923,8 +1933,75 @@ static struct drm_driver msm_driver = {
#ifdef CONFIG_PM_SLEEP
static int msm_pm_suspend(struct device *dev)
{
- struct drm_device *ddev = dev_get_drvdata(dev);
+ struct drm_device *ddev;
+ struct drm_modeset_acquire_ctx *ctx;
+ struct drm_connector *conn;
+ struct drm_atomic_state *state;
+ struct drm_crtc_state *crtc_state;
+ struct msm_drm_private *priv;
+ int ret = 0;
+
+ if (!dev)
+ return -EINVAL;
+
+ ddev = dev_get_drvdata(dev);
+ if (!ddev || !ddev->dev_private)
+ return -EINVAL;
+
+ priv = ddev->dev_private;
+ SDE_EVT32(0);
+ /* acquire modeset lock(s) */
+ drm_modeset_lock_all(ddev);
+ ctx = ddev->mode_config.acquire_ctx;
+
+ /* save current state for resume */
+ if (priv->suspend_state)
+ drm_atomic_state_free(priv->suspend_state);
+ priv->suspend_state = drm_atomic_helper_duplicate_state(ddev, ctx);
+ if (IS_ERR_OR_NULL(priv->suspend_state)) {
+ DRM_ERROR("failed to back up suspend state\n");
+ priv->suspend_state = NULL;
+ goto unlock;
+ }
+
+ /* create atomic state to disable all CRTCs */
+ state = drm_atomic_state_alloc(ddev);
+ if (IS_ERR_OR_NULL(state)) {
+ DRM_ERROR("failed to allocate crtc disable state\n");
+ goto unlock;
+ }
+
+ state->acquire_ctx = ctx;
+ drm_for_each_connector(conn, ddev) {
+
+ if (!conn->state || !conn->state->crtc ||
+ conn->dpms != DRM_MODE_DPMS_ON)
+ continue;
+
+ /* force CRTC to be inactive */
+ crtc_state = drm_atomic_get_crtc_state(state,
+ conn->state->crtc);
+ if (IS_ERR_OR_NULL(crtc_state)) {
+ DRM_ERROR("failed to get crtc %d state\n",
+ conn->state->crtc->base.id);
+ drm_atomic_state_free(state);
+ goto unlock;
+ }
+ crtc_state->active = false;
+ }
+
+ /* commit the "disable all" state */
+ ret = drm_atomic_commit(state);
+ if (ret < 0) {
+ DRM_ERROR("failed to disable crtcs, %d\n", ret);
+ drm_atomic_state_free(state);
+ }
+
+unlock:
+ drm_modeset_unlock_all(ddev);
+
+ /* disable hot-plug polling */
drm_kms_helper_poll_disable(ddev);
return 0;
@@ -1932,8 +2009,38 @@ static int msm_pm_suspend(struct device *dev)
static int msm_pm_resume(struct device *dev)
{
- struct drm_device *ddev = dev_get_drvdata(dev);
+ struct drm_device *ddev;
+ struct msm_drm_private *priv;
+ int ret;
+
+ if (!dev)
+ return -EINVAL;
+
+ ddev = dev_get_drvdata(dev);
+ if (!ddev || !ddev->dev_private)
+ return -EINVAL;
+
+ priv = ddev->dev_private;
+
+ SDE_EVT32(priv->suspend_state != NULL);
+
+ drm_mode_config_reset(ddev);
+
+ drm_modeset_lock_all(ddev);
+
+ if (priv->suspend_state) {
+ priv->suspend_state->acquire_ctx =
+ ddev->mode_config.acquire_ctx;
+ ret = drm_atomic_commit(priv->suspend_state);
+ if (ret < 0) {
+ DRM_ERROR("failed to restore state, %d\n", ret);
+ drm_atomic_state_free(priv->suspend_state);
+ }
+ priv->suspend_state = NULL;
+ }
+ drm_modeset_unlock_all(ddev);
+ /* enable hot-plug polling */
drm_kms_helper_poll_enable(ddev);
return 0;
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 08868fce1cb0..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
@@ -367,6 +368,9 @@ struct msm_drm_private {
struct msm_vblank_ctrl vblank_ctrl;
+ /* saved atomic state during system suspend */
+ struct drm_atomic_state *suspend_state;
+
/* list of clients waiting for events */
struct list_head client_event_list;
};
@@ -414,6 +418,15 @@ void __msm_fence_worker(struct work_struct *work);
(_cb)->func = _func; \
} while (0)
+static inline bool msm_is_suspend_state(struct drm_device *dev)
+{
+ if (!dev || !dev->dev_private)
+ return false;
+
+ return ((struct msm_drm_private *)dev->dev_private)->suspend_state !=
+ NULL;
+}
+
int msm_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state, bool async);
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index df9ddadc5c5c..0cd458fd184b 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -149,7 +149,6 @@ struct msm_gem_submit {
uint32_t fence;
int ring;
u32 flags;
- bool valid;
uint64_t profile_buf_iova;
struct drm_msm_gem_submit_profile_buffer *profile_buf;
bool secure;
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index b73379aa9ed7..f2b6aa29b410 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -212,15 +212,8 @@ static int submit_validate_objects(struct msm_gpu *gpu,
int contended, slow_locked = -1, i, ret = 0;
retry:
- submit->valid = true;
-
for (i = 0; i < submit->nr_bos; i++) {
struct msm_gem_object *msm_obj = submit->bos[i].obj;
- struct msm_gem_address_space *aspace;
- uint64_t iova;
-
- aspace = (msm_obj->flags & MSM_BO_SECURE) ?
- gpu->secure_aspace : submit->aspace;
if (slow_locked == i)
slow_locked = -1;
@@ -247,28 +240,6 @@ retry:
goto fail;
}
}
-
- /* if locking succeeded, pin bo: */
- ret = msm_gem_get_iova(&msm_obj->base, aspace, &iova);
-
- /* this would break the logic in the fail path.. there is no
- * reason for this to happen, but just to be on the safe side
- * let's notice if this starts happening in the future:
- */
- WARN_ON(ret == -EDEADLK);
-
- if (ret)
- goto fail;
-
- submit->bos[i].flags |= BO_PINNED;
-
- if (iova == submit->bos[i].iova) {
- submit->bos[i].flags |= BO_VALID;
- } else {
- submit->bos[i].iova = iova;
- submit->bos[i].flags &= ~BO_VALID;
- submit->valid = false;
- }
}
ww_acquire_done(&submit->ticket);
@@ -297,9 +268,14 @@ fail:
return ret;
}
-static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
+static int submit_bo(struct msm_gpu *gpu,
+ struct msm_gem_submit *submit, uint32_t idx,
struct msm_gem_object **obj, uint64_t *iova, bool *valid)
{
+ struct msm_gem_object *msm_obj;
+ struct msm_gem_address_space *aspace;
+ int ret;
+
if (idx >= submit->nr_bos) {
DRM_ERROR("invalid buffer index: %u (out of %u)\n",
idx, submit->nr_bos);
@@ -308,6 +284,39 @@ static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
if (obj)
*obj = submit->bos[idx].obj;
+
+ /* Only map and pin if the caller needs either the iova or valid */
+ if (!iova && !valid)
+ return 0;
+
+ if (!(submit->bos[idx].flags & BO_PINNED)) {
+ uint64_t buf_iova;
+
+ msm_obj = submit->bos[idx].obj;
+ aspace = (msm_obj->flags & MSM_BO_SECURE) ?
+ gpu->secure_aspace : submit->aspace;
+
+ ret = msm_gem_get_iova(&msm_obj->base, aspace, &buf_iova);
+
+ /* this would break the logic in the fail path.. there is no
+ * reason for this to happen, but just to be on the safe side
+ * let's notice if this starts happening in the future:
+ */
+ WARN_ON(ret == -EDEADLK);
+
+ if (ret)
+ return ret;
+
+ submit->bos[idx].flags |= BO_PINNED;
+
+ if (buf_iova == submit->bos[idx].iova) {
+ submit->bos[idx].flags |= BO_VALID;
+ } else {
+ submit->bos[idx].iova = buf_iova;
+ submit->bos[idx].flags &= ~BO_VALID;
+ }
+ }
+
if (iova)
*iova = submit->bos[idx].iova;
if (valid)
@@ -317,8 +326,10 @@ static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
}
/* process the reloc's and patch up the cmdstream as needed: */
-static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj,
- uint32_t offset, uint32_t nr_relocs, uint64_t relocs)
+static int submit_reloc(struct msm_gpu *gpu,
+ struct msm_gem_submit *submit,
+ struct msm_gem_object *obj, uint32_t offset,
+ uint32_t nr_relocs, uint64_t relocs)
{
uint32_t i, last_offset = 0;
uint32_t *ptr;
@@ -334,6 +345,9 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob
return -EINVAL;
}
+ if (nr_relocs == 0)
+ return 0;
+
/* For now, just map the entire thing. Eventually we probably
* to do it page-by-page, w/ kmap() if not vmap()d..
*/
@@ -372,7 +386,8 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob
return -EINVAL;
}
- ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
+ ret = submit_bo(gpu, submit, submit_reloc.reloc_idx,
+ NULL, &iova, &valid);
if (ret)
return ret;
@@ -482,7 +497,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
goto out;
}
- ret = submit_bo(submit, submit_cmd.submit_idx,
+ ret = submit_bo(gpu, submit, submit_cmd.submit_idx,
&msm_obj, &iova, NULL);
if (ret)
goto out;
@@ -515,11 +530,9 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
+ submit_cmd.submit_offset;
}
- if (submit->valid)
- continue;
-
- ret = submit_reloc(submit, msm_obj, submit_cmd.submit_offset,
- submit_cmd.nr_relocs, submit_cmd.relocs);
+ ret = submit_reloc(gpu, submit, msm_obj,
+ submit_cmd.submit_offset, submit_cmd.nr_relocs,
+ submit_cmd.relocs);
if (ret)
goto out;
}
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 8073898e4275..7c109fdab545 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -863,7 +863,14 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
gpu->dev = drm;
gpu->funcs = funcs;
gpu->name = name;
- gpu->inactive = true;
+ /*
+ * Set the inactive flag to false, so that when the retire worker
+ * kicks in from the init path, it knows that it has to turn off the
+ * clocks. This should be fine to do since this is the init sequence
+ * and we have an init_lock in msm_open() to protect against bad things
+ * from happening.
+ */
+ gpu->inactive = false;
INIT_LIST_HEAD(&gpu->active_list);
INIT_WORK(&gpu->retire_work, retire_worker);
@@ -897,6 +904,7 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
ret = devm_request_irq(&pdev->dev, gpu->irq, irq_handler,
IRQF_TRIGGER_HIGH, gpu->name, gpu);
if (ret) {
+ gpu->irq = ret;
dev_err(drm->dev, "failed to request IRQ%u: %d\n", gpu->irq, ret);
goto fail;
}
@@ -1007,6 +1015,11 @@ void msm_gpu_cleanup(struct msm_gpu *gpu)
WARN_ON(!list_empty(&gpu->active_list));
+ if (gpu->irq >= 0) {
+ disable_irq(gpu->irq);
+ devm_free_irq(&pdev->dev, gpu->irq, gpu);
+ }
+
bs_fini(gpu);
for (i = 0; i < ARRAY_SIZE(gpu->rb); i++)
@@ -1022,4 +1035,22 @@ void msm_gpu_cleanup(struct msm_gpu *gpu)
msm_gpu_destroy_address_space(gpu->aspace);
msm_gpu_destroy_address_space(gpu->secure_aspace);
+
+ if (gpu->gpu_reg)
+ devm_regulator_put(gpu->gpu_reg);
+
+ if (gpu->gpu_cx)
+ devm_regulator_put(gpu->gpu_cx);
+
+ if (gpu->ebi1_clk)
+ devm_clk_put(&pdev->dev, gpu->ebi1_clk);
+
+ for (i = gpu->nr_clocks - 1; i >= 0; i--)
+ if (gpu->grp_clks[i])
+ devm_clk_put(&pdev->dev, gpu->grp_clks[i]);
+
+ devm_kfree(&pdev->dev, gpu->grp_clks);
+
+ if (gpu->mmio)
+ devm_iounmap(&pdev->dev, gpu->mmio);
}
diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h
index 8148d3e9e850..cd3a710f8f27 100644
--- a/drivers/gpu/drm/msm/msm_mmu.h
+++ b/drivers/gpu/drm/msm/msm_mmu.h
@@ -46,6 +46,8 @@ struct msm_mmu_funcs {
void (*destroy)(struct msm_mmu *mmu);
void (*enable)(struct msm_mmu *mmu);
void (*disable)(struct msm_mmu *mmu);
+ int (*set_property)(struct msm_mmu *mmu,
+ enum iommu_attr attr, void *data);
};
struct msm_mmu {
diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c
index eb68eb977aa7..4247243055b6 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -170,12 +170,36 @@ static void msm_smmu_destroy(struct msm_mmu *mmu)
kfree(smmu);
}
+/* user can call this API to set the attribute of smmu*/
+static int msm_smmu_set_property(struct msm_mmu *mmu,
+ enum iommu_attr attr, void *data)
+{
+ struct msm_smmu *smmu = to_msm_smmu(mmu);
+ struct msm_smmu_client *client = msm_smmu_to_client(smmu);
+ struct iommu_domain *domain;
+ int ret = 0;
+
+ if (!client)
+ return -EINVAL;
+
+ domain = client->mmu_mapping->domain;
+ if (!domain)
+ return -EINVAL;
+
+ ret = iommu_domain_set_attr(domain, attr, data);
+ if (ret)
+ DRM_ERROR("set domain attribute failed\n");
+
+ return ret;
+}
+
static const struct msm_mmu_funcs funcs = {
.attach = msm_smmu_attach,
.detach = msm_smmu_detach,
.map = msm_smmu_map,
.unmap = msm_smmu_unmap,
.destroy = msm_smmu_destroy,
+ .set_property = msm_smmu_set_property,
};
static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = {
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index ef7492817983..a0f6b5c6a732 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -344,8 +344,8 @@ static void sde_cp_crtc_install_immutable_property(struct drm_crtc *crtc,
prop = priv->cp_property[feature];
if (!prop) {
- prop = drm_property_create(crtc->dev, DRM_MODE_PROP_IMMUTABLE,
- name, 0);
+ prop = drm_property_create_range(crtc->dev,
+ DRM_MODE_PROP_IMMUTABLE, name, 0, 1);
if (!prop) {
DRM_ERROR("property create failed: %s\n", name);
kfree(prop_node);
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 875513d2840f..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)
{
@@ -90,6 +98,50 @@ int sde_connector_pre_kickoff(struct drm_connector *connector)
return rc;
}
+enum sde_csc_type sde_connector_get_csc_type(struct drm_connector *conn)
+{
+ struct sde_connector *c_conn;
+
+ if (!conn) {
+ SDE_ERROR("invalid argument\n");
+ return -EINVAL;
+ }
+
+ c_conn = to_sde_connector(conn);
+
+ if (!c_conn->display) {
+ SDE_ERROR("invalid argument\n");
+ return -EINVAL;
+ }
+
+ if (!c_conn->ops.get_csc_type)
+ return SDE_CSC_RGB2YUV_601L;
+
+ return c_conn->ops.get_csc_type(conn, c_conn->display);
+}
+
+bool sde_connector_mode_needs_full_range(struct drm_connector *connector)
+{
+ struct sde_connector *c_conn;
+
+ if (!connector) {
+ SDE_ERROR("invalid argument\n");
+ return false;
+ }
+
+ c_conn = to_sde_connector(connector);
+
+ if (!c_conn->display) {
+ SDE_ERROR("invalid argument\n");
+ return false;
+ }
+
+ if (!c_conn->ops.mode_needs_full_range)
+ return false;
+
+ return c_conn->ops.mode_needs_full_range(c_conn->display);
+}
+
static void sde_connector_destroy(struct drm_connector *connector)
{
struct sde_connector *c_conn;
@@ -111,6 +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);
@@ -309,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,
@@ -335,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);
@@ -360,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) {
@@ -457,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)
@@ -514,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,
@@ -604,6 +791,7 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
struct sde_kms *sde_kms;
struct sde_kms_info *info;
struct sde_connector *c_conn = NULL;
+ struct sde_splash_info *sinfo;
int rc;
if (!dev || !dev->dev_private || !encoder) {
@@ -636,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] =
@@ -669,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);
@@ -748,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");
@@ -757,6 +957,10 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
SDE_DEBUG("connector %d attach encoder %d\n",
c_conn->base.base.id, encoder->base.id);
+ sinfo = &sde_kms->splash_info;
+ if (sinfo && sinfo->handoff)
+ sde_splash_setup_connector_count(sinfo, connector_type);
+
priv->connectors[priv->num_connectors++] = &c_conn->base;
return &c_conn->base;
@@ -770,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 8257f29bd4b8..f9b8c3966d74 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -135,6 +135,38 @@ struct sde_connector_ops {
int (*pre_kickoff)(struct drm_connector *connector,
void *display,
struct msm_display_kickoff_params *params);
+
+ /**
+ * mode_needs_full_range - does the mode need full range
+ * quantization
+ * @display: Pointer to private display structure
+ * Returns: true or false based on whether full range is needed
+ */
+ bool (*mode_needs_full_range)(void *display);
+
+ /**
+ * get_csc_type - returns the CSC type to be used
+ * by the CDM block based on HDR state
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display structure
+ * Returns: type of CSC matrix to be used
+ */
+ enum sde_csc_type (*get_csc_type)(struct drm_connector *connector,
+ void *display);
+
+ /**
+ * set_power - update dpms setting
+ * @connector: Pointer to drm connector structure
+ * @power_mode: One of the following,
+ * SDE_MODE_DPMS_ON
+ * SDE_MODE_DPMS_LP1
+ * SDE_MODE_DPMS_LP2
+ * SDE_MODE_DPMS_OFF
+ * @display: Pointer to private display structure
+ * Returns: Zero on success
+ */
+ int (*set_power)(struct drm_connector *connector,
+ int power_mode, void *display);
};
/**
@@ -147,8 +179,12 @@ struct sde_connector_ops {
* @mmu_secure: MMU id for secure buffers
* @mmu_unsecure: MMU id for unsecure buffers
* @name: ASCII name of connector
+ * @lock: Mutex lock object for this structure
* @retire_fence: Retire fence reference
* @ops: Local callback function pointer table
+ * @dpms_mode: DPMS property setting from user space
+ * @lp_mode: LP property setting from user space
+ * @last_panel_power_mode: Last consolidated dpms/lp mode setting
* @property_info: Private structure for generic property handling
* @property_data: Array of private data for generic property handling
* @blob_caps: Pointer to blob structure for 'capabilities' property
@@ -167,8 +203,12 @@ struct sde_connector {
char name[SDE_CONNECTOR_NAME_SIZE];
+ struct mutex lock;
struct sde_fence retire_fence;
struct sde_connector_ops ops;
+ int dpms_mode;
+ int lp_mode;
+ int last_panel_power_mode;
struct msm_property_info property_info;
struct msm_property_data property_data[CONNECTOR_PROP_COUNT];
@@ -327,5 +367,28 @@ int sde_connector_get_info(struct drm_connector *connector,
*/
int sde_connector_pre_kickoff(struct drm_connector *connector);
+/**
+ * sde_connector_mode_needs_full_range - query quantization type
+ * for the connector mode
+ * @connector: Pointer to drm connector object
+ * Returns: true OR false based on connector mode
+ */
+bool sde_connector_mode_needs_full_range(struct drm_connector *connector);
+
+/**
+ * sde_connector_get_csc_type - query csc type
+ * to be used for the connector
+ * @connector: Pointer to drm connector object
+ * Returns: csc type based on connector HDR state
+ */
+enum sde_csc_type sde_connector_get_csc_type(struct drm_connector *conn);
+
+/**
+ * sde_connector_get_dpms - query dpms setting
+ * @connector: Pointer to drm connector structure
+ * Returns: Current DPMS setting for connector
+ */
+int sde_connector_get_dpms(struct drm_connector *connector);
+
#endif /* _SDE_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index dbfc2dd11a17..83c8982b2e00 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -32,7 +32,7 @@ static void sde_core_irq_callback_handler(void *arg, int irq_idx)
struct sde_irq_callback *cb;
unsigned long irq_flags;
- SDE_DEBUG("irq_idx=%d\n", irq_idx);
+ pr_debug("irq_idx=%d\n", irq_idx);
if (list_empty(&irq_obj->irq_cb_tbl[irq_idx]))
SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 5323c0194594..a0417a0dd12e 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -57,7 +57,17 @@
static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
{
- struct msm_drm_private *priv = crtc->dev->dev_private;
+ struct msm_drm_private *priv;
+
+ if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
+ SDE_ERROR("invalid crtc\n");
+ return NULL;
+ }
+ priv = crtc->dev->dev_private;
+ if (!priv || !priv->kms) {
+ SDE_ERROR("invalid kms\n");
+ return NULL;
+ }
return to_sde_kms(priv->kms);
}
@@ -77,10 +87,10 @@ static void sde_crtc_destroy(struct drm_crtc *crtc)
sde_cp_crtc_destroy_properties(crtc);
debugfs_remove_recursive(sde_crtc->debugfs_root);
- mutex_destroy(&sde_crtc->crtc_lock);
sde_fence_deinit(&sde_crtc->output_fence);
drm_crtc_cleanup(crtc);
+ mutex_destroy(&sde_crtc->crtc_lock);
kfree(sde_crtc);
}
@@ -900,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)
@@ -941,6 +960,120 @@ end:
}
/**
+ * _sde_crtc_vblank_enable_nolock - update power resource and vblank request
+ * @sde_crtc: Pointer to sde crtc structure
+ * @enable: Whether to enable/disable vblanks
+ *
+ * @Return: error code
+ */
+static int _sde_crtc_vblank_enable_no_lock(
+ struct sde_crtc *sde_crtc, bool enable)
+{
+ struct drm_device *dev;
+ struct drm_crtc *crtc;
+ struct drm_encoder *enc;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
+ int ret = 0;
+
+ if (!sde_crtc) {
+ SDE_ERROR("invalid crtc\n");
+ return -EINVAL;
+ }
+
+ crtc = &sde_crtc->base;
+ dev = crtc->dev;
+ priv = dev->dev_private;
+
+ if (!priv->kms) {
+ SDE_ERROR("invalid kms\n");
+ return -EINVAL;
+ }
+ sde_kms = to_sde_kms(priv->kms);
+
+ if (enable) {
+ ret = sde_power_resource_enable(&priv->phandle,
+ sde_kms->core_client, true);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
+ if (enc->crtc != crtc)
+ continue;
+
+ SDE_EVT32(DRMID(crtc), DRMID(enc), enable);
+
+ sde_encoder_register_vblank_callback(enc,
+ sde_crtc_vblank_cb, (void *)crtc);
+ }
+ } else {
+ list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
+ if (enc->crtc != crtc)
+ continue;
+
+ SDE_EVT32(DRMID(crtc), DRMID(enc), enable);
+
+ sde_encoder_register_vblank_callback(enc, NULL, NULL);
+ }
+ ret = sde_power_resource_enable(&priv->phandle,
+ sde_kms->core_client, false);
+ }
+
+ return ret;
+}
+
+/**
+ * _sde_crtc_set_suspend - notify crtc of suspend enable/disable
+ * @crtc: Pointer to drm crtc object
+ * @enable: true to enable suspend, false to indicate resume
+ */
+static void _sde_crtc_set_suspend(struct drm_crtc *crtc, bool enable)
+{
+ struct sde_crtc *sde_crtc;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
+
+ if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
+ SDE_ERROR("invalid crtc\n");
+ return;
+ }
+ sde_crtc = to_sde_crtc(crtc);
+ priv = crtc->dev->dev_private;
+
+ if (!priv->kms) {
+ SDE_ERROR("invalid crtc kms\n");
+ return;
+ }
+ sde_kms = to_sde_kms(priv->kms);
+
+ SDE_DEBUG("crtc%d suspend = %d\n", crtc->base.id, enable);
+
+ mutex_lock(&sde_crtc->crtc_lock);
+
+ /*
+ * Update CP on suspend/resume transitions
+ */
+ if (enable && !sde_crtc->suspend)
+ sde_cp_crtc_suspend(crtc);
+ else if (!enable && sde_crtc->suspend)
+ sde_cp_crtc_resume(crtc);
+
+ /*
+ * If the vblank refcount != 0, release a power reference on suspend
+ * and take it back during resume (if it is still != 0).
+ */
+ if (sde_crtc->suspend == enable)
+ SDE_DEBUG("crtc%d suspend already set to %d, ignoring update\n",
+ crtc->base.id, enable);
+ else if (sde_crtc->enabled && sde_crtc->vblank_requested)
+ _sde_crtc_vblank_enable_no_lock(sde_crtc, !enable);
+
+ sde_crtc->suspend = enable;
+
+ mutex_unlock(&sde_crtc->crtc_lock);
+}
+
+/**
* sde_crtc_duplicate_state - state duplicate hook
* @crtc: Pointer to drm crtc structure
* @Returns: Pointer to new drm_crtc_state structure
@@ -990,6 +1123,10 @@ static void sde_crtc_reset(struct drm_crtc *crtc)
return;
}
+ /* revert suspend actions, if necessary */
+ if (msm_is_suspend_state(crtc->dev))
+ _sde_crtc_set_suspend(crtc, false);
+
/* remove previous state, if present */
if (crtc->state) {
sde_crtc_destroy_state(crtc, crtc->state);
@@ -1015,37 +1152,43 @@ static void sde_crtc_reset(struct drm_crtc *crtc)
static void sde_crtc_disable(struct drm_crtc *crtc)
{
- struct msm_drm_private *priv;
- struct sde_crtc *sde_crtc;
struct drm_encoder *encoder;
+ struct sde_crtc *sde_crtc;
struct sde_kms *sde_kms;
+ struct msm_drm_private *priv;
+ int ret = 0;
- if (!crtc) {
+ if (!crtc || !crtc->dev || !crtc->state) {
SDE_ERROR("invalid crtc\n");
return;
}
sde_crtc = to_sde_crtc(crtc);
sde_kms = _sde_crtc_get_kms(crtc);
+ if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev_private) {
+ SDE_ERROR("invalid kms handle\n");
+ return;
+ }
priv = sde_kms->dev->dev_private;
SDE_DEBUG("crtc%d\n", crtc->base.id);
+ if (msm_is_suspend_state(crtc->dev))
+ _sde_crtc_set_suspend(crtc, true);
+
mutex_lock(&sde_crtc->crtc_lock);
- SDE_EVT32(DRMID(crtc));
+ SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
+ sde_crtc->vblank_requested);
- if (atomic_read(&sde_crtc->vblank_refcount)) {
- SDE_ERROR("crtc%d invalid vblank refcount\n",
- crtc->base.id);
- SDE_EVT32(DRMID(crtc));
- drm_for_each_encoder(encoder, crtc->dev) {
- if (encoder->crtc != crtc)
- continue;
- sde_encoder_register_vblank_callback(encoder, NULL,
- NULL);
- }
- atomic_set(&sde_crtc->vblank_refcount, 0);
+ if (sde_crtc->enabled && !sde_crtc->suspend &&
+ sde_crtc->vblank_requested) {
+ ret = _sde_crtc_vblank_enable_no_lock(sde_crtc, false);
+ if (ret)
+ SDE_ERROR("%s vblank enable failed: %d\n",
+ sde_crtc->name, ret);
}
+ sde_crtc->enabled = false;
+
if (atomic_read(&sde_crtc->frame_pending)) {
/* release bandwidth and other resources */
SDE_ERROR("crtc%d invalid frame pending\n",
@@ -1080,6 +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");
@@ -1108,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);
@@ -1154,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");
@@ -1172,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 */
@@ -1205,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)
@@ -1219,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) {
@@ -1229,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);
}
@@ -1256,45 +1425,77 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
goto end;
}
+ /*
+ * enforce pipe priority restrictions
+ * use pstates sorted by stage to check planes on same stage
+ * we assume that all pipes are in source split so its valid to compare
+ * without taking into account left/right mixer placement
+ */
+ for (i = 1; i < cnt; i++) {
+ struct plane_state *prv_pstate, *cur_pstate;
+ int32_t prv_x, cur_x, prv_id, cur_id;
+
+ prv_pstate = &pstates[i - 1];
+ cur_pstate = &pstates[i];
+ if (prv_pstate->stage != cur_pstate->stage)
+ continue;
+
+ prv_x = prv_pstate->drm_pstate->crtc_x;
+ cur_x = cur_pstate->drm_pstate->crtc_x;
+ prv_id = prv_pstate->sde_pstate->base.plane->base.id;
+ cur_id = cur_pstate->sde_pstate->base.plane->base.id;
+
+ /*
+ * Planes are enumerated in pipe-priority order such that planes
+ * with lower drm_id must be left-most in a shared blend-stage
+ * when using source split.
+ */
+ if (cur_x > prv_x && cur_id < prv_id) {
+ SDE_ERROR(
+ "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
+ cur_pstate->stage, cur_id, cur_x,
+ prv_id, prv_x);
+ rc = -EINVAL;
+ goto end;
+ } else if (cur_x < prv_x && cur_id > prv_id) {
+ SDE_ERROR(
+ "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
+ cur_pstate->stage, prv_id, prv_x,
+ cur_id, cur_x);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+
+
end:
return rc;
}
int sde_crtc_vblank(struct drm_crtc *crtc, bool en)
{
- struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
- struct drm_encoder *encoder;
- struct drm_device *dev = crtc->dev;
+ struct sde_crtc *sde_crtc;
+ int ret;
- if (en && atomic_inc_return(&sde_crtc->vblank_refcount) == 1) {
- SDE_DEBUG("crtc%d vblank enable\n", crtc->base.id);
- } else if (!en && atomic_read(&sde_crtc->vblank_refcount) < 1) {
- SDE_ERROR("crtc%d invalid vblank disable\n", crtc->base.id);
+ if (!crtc) {
+ SDE_ERROR("invalid crtc\n");
return -EINVAL;
- } else if (!en && atomic_dec_return(&sde_crtc->vblank_refcount) == 0) {
- SDE_DEBUG("crtc%d vblank disable\n", crtc->base.id);
- } else {
- SDE_DEBUG("crtc%d vblank %s refcount:%d\n",
- crtc->base.id,
- en ? "enable" : "disable",
- atomic_read(&sde_crtc->vblank_refcount));
- return 0;
}
+ sde_crtc = to_sde_crtc(crtc);
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- if (encoder->crtc != crtc)
- continue;
-
- SDE_EVT32(DRMID(crtc), en);
-
- if (en)
- sde_encoder_register_vblank_callback(encoder,
- sde_crtc_vblank_cb, (void *)crtc);
- else
- sde_encoder_register_vblank_callback(encoder, NULL,
- NULL);
+ mutex_lock(&sde_crtc->crtc_lock);
+ SDE_EVT32(DRMID(&sde_crtc->base), en, sde_crtc->enabled,
+ sde_crtc->suspend, sde_crtc->vblank_requested);
+ if (sde_crtc->enabled && !sde_crtc->suspend) {
+ ret = _sde_crtc_vblank_enable_no_lock(sde_crtc, en);
+ if (ret)
+ SDE_ERROR("%s vblank enable failed: %d\n",
+ sde_crtc->name, ret);
}
+ sde_crtc->vblank_requested = en;
+ mutex_unlock(&sde_crtc->crtc_lock);
+
return 0;
}
@@ -1396,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);
}
@@ -1608,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);
@@ -1737,8 +1937,8 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev,
crtc = &sde_crtc->base;
crtc->dev = dev;
- atomic_set(&sde_crtc->vblank_refcount, 0);
+ mutex_init(&sde_crtc->crtc_lock);
spin_lock_init(&sde_crtc->spin_lock);
atomic_set(&sde_crtc->frame_pending, 0);
@@ -1760,7 +1960,6 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev,
snprintf(sde_crtc->name, SDE_CRTC_NAME_SIZE, "crtc%u", crtc->base.id);
/* initialize output fence support */
- mutex_init(&sde_crtc->crtc_lock);
sde_fence_init(&sde_crtc->output_fence, sde_crtc->name, crtc->base.id);
/* initialize debugfs support */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index aaa815c76c4e..0eed61580cd8 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -81,7 +81,11 @@ struct sde_crtc_frame_event {
* @debugfs_root : Parent of debugfs node
* @vblank_cb_count : count of vblank callback since last reset
* @vblank_cb_time : ktime at vblank count reset
- * @vblank_refcount : reference count for vblank enable request
+ * @vblank_requested : whether the user has requested vblank events
+ * @suspend : whether or not a suspend operation is in progress
+ * @enabled : whether the SDE CRTC is currently enabled. updated in the
+ * commit-thread, not state-swap time which is earlier, so
+ * safe to make decisions on during VBLANK on/off work
* @feature_list : list of color processing features supported on a crtc
* @active_list : list of color processing features are active
* @dirty_list : list of color processing features are dirty
@@ -116,7 +120,9 @@ struct sde_crtc {
u32 vblank_cb_count;
ktime_t vblank_cb_time;
- atomic_t vblank_refcount;
+ bool vblank_requested;
+ bool suspend;
+ bool enabled;
struct list_head feature_list;
struct list_head active_list;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 97c9f8baea6d..cb8b349e72c7 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -80,6 +80,42 @@ static struct sde_csc_cfg sde_csc_10bit_convert[SDE_MAX_CSC] = {
{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
},
+
+ [SDE_CSC_RGB2YUV_709L] = {
+ {
+ TO_S15D16(0x005d), TO_S15D16(0x013a), TO_S15D16(0x0020),
+ TO_S15D16(0xffcc), TO_S15D16(0xff53), TO_S15D16(0x00e1),
+ TO_S15D16(0x00e1), TO_S15D16(0xff34), TO_S15D16(0xffeb),
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0040, 0x0200, 0x0200,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ { 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,},
+ },
+
+ [SDE_CSC_RGB2YUV_2020L] = {
+ {
+ TO_S15D16(0x0073), TO_S15D16(0x0129), TO_S15D16(0x001a),
+ TO_S15D16(0xffc1), TO_S15D16(0xff5e), TO_S15D16(0x00e0),
+ TO_S15D16(0x00e0), TO_S15D16(0xff32), TO_S15D16(0xffee),
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0040, 0x0200, 0x0200,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ { 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,},
+ },
+
+ [SDE_CSC_RGB2YUV_2020FR] = {
+ {
+ TO_S15D16(0x0086), TO_S15D16(0x015b), TO_S15D16(0x001e),
+ TO_S15D16(0xffb9), TO_S15D16(0xff47), TO_S15D16(0x0100),
+ TO_S15D16(0x0100), TO_S15D16(0xff15), TO_S15D16(0xffeb),
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0x0200, 0x0200,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ },
};
/**
@@ -470,11 +506,6 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
SDE_EVT32(DRMID(drm_enc));
- if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
- SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id);
- del_timer_sync(&sde_enc->frame_done_timer);
- }
-
for (i = 0; i < sde_enc->num_phys_encs; i++) {
struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
@@ -487,6 +518,12 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
}
}
+ /* after phys waits for frame-done, should be no more frames pending */
+ if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
+ SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id);
+ del_timer_sync(&sde_enc->frame_done_timer);
+ }
+
if (sde_enc->cur_master && sde_enc->cur_master->ops.disable)
sde_enc->cur_master->ops.disable(sde_enc->cur_master);
@@ -826,7 +863,12 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
{
struct sde_encoder_virt *sde_enc;
struct sde_encoder_phys *phys;
+ struct drm_connector *conn_mas = NULL;
unsigned int i;
+ enum sde_csc_type conn_csc;
+ struct drm_display_mode *mode;
+ struct sde_hw_cdm *hw_cdm;
+ int mode_is_yuv = 0;
int rc;
if (!drm_enc) {
@@ -846,11 +888,46 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
}
if (sde_enc->cur_master && sde_enc->cur_master->connector) {
- rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector);
+ conn_mas = sde_enc->cur_master->connector;
+ rc = sde_connector_pre_kickoff(conn_mas);
if (rc)
- SDE_ERROR_ENC(sde_enc, "kickoff conn%d failed rc %d\n",
- sde_enc->cur_master->connector->base.id,
- rc);
+ SDE_ERROR_ENC(sde_enc,
+ "kickoff conn%d failed rc %d\n",
+ conn_mas->base.id,
+ rc);
+
+ for (i = 0; i < sde_enc->num_phys_encs; i++) {
+ phys = sde_enc->phys_encs[i];
+ if (phys) {
+ mode = &phys->cached_mode;
+ mode_is_yuv = (mode->private_flags &
+ MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420);
+ }
+ /**
+ * Check the CSC matrix type to which the
+ * CDM CSC matrix should be updated to based
+ * on the connector HDR state
+ */
+ conn_csc = sde_connector_get_csc_type(conn_mas);
+ if (phys && mode_is_yuv) {
+ if (phys->enc_cdm_csc != conn_csc) {
+ hw_cdm = phys->hw_cdm;
+ rc = hw_cdm->ops.setup_csc_data(hw_cdm,
+ &sde_csc_10bit_convert[conn_csc]);
+
+ if (rc)
+ SDE_ERROR_ENC(sde_enc,
+ "CSC setup failed rc %d\n",
+ rc);
+ SDE_DEBUG_ENC(sde_enc,
+ "updating CSC %d to %d\n",
+ phys->enc_cdm_csc,
+ conn_csc);
+ phys->enc_cdm_csc = conn_csc;
+
+ }
+ }
+ }
}
}
@@ -1417,6 +1494,7 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc,
struct sde_encoder_virt *sde_enc = NULL;
struct sde_hw_cdm *hw_cdm = phys_enc->hw_cdm;
struct sde_hw_cdm_cfg *cdm_cfg = &phys_enc->cdm_cfg;
+ struct drm_connector *connector = phys_enc->connector;
int ret;
u32 csc_type = 0;
@@ -1476,10 +1554,26 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc,
cdm_cfg->h_cdwn_type,
cdm_cfg->v_cdwn_type);
- if (output_type == CDM_CDWN_OUTPUT_HDMI)
- csc_type = SDE_CSC_RGB2YUV_601FR;
- else if (output_type == CDM_CDWN_OUTPUT_WB)
+ /**
+ * Choose CSC matrix based on following rules:
+ * 1. If connector supports quantization select,
+ * pick Full-Range for better quality.
+ * 2. If non-CEA mode, then pick Full-Range as per CEA spec
+ * 3. Otherwise, pick Limited-Range as all other CEA modes
+ * need a limited range
+ */
+
+ if (output_type == CDM_CDWN_OUTPUT_HDMI) {
+ if (connector && connector->yuv_qs)
+ csc_type = SDE_CSC_RGB2YUV_601FR;
+ else if (connector &&
+ sde_connector_mode_needs_full_range(connector))
+ csc_type = SDE_CSC_RGB2YUV_601FR;
+ else
+ csc_type = SDE_CSC_RGB2YUV_601L;
+ } else if (output_type == CDM_CDWN_OUTPUT_WB) {
csc_type = SDE_CSC_RGB2YUV_601L;
+ }
if (hw_cdm && hw_cdm->ops.setup_csc_data) {
ret = hw_cdm->ops.setup_csc_data(hw_cdm,
@@ -1490,6 +1584,9 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc,
}
}
+ /* Cache the CSC default matrix type */
+ phys_enc->enc_cdm_csc = csc_type;
+
if (hw_cdm && hw_cdm->ops.setup_cdwn) {
ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg);
if (ret < 0) {
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 20f125155de3..aec844d640bd 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -174,6 +174,7 @@ enum sde_intr_idx {
* @split_role: Role to play in a split-panel configuration
* @intf_mode: Interface mode
* @intf_idx: Interface index on sde hardware
+ * @enc_cdm_csc: Cached CSC type of CDM block
* @enc_spinlock: Virtual-Encoder-Wide Spin Lock for IRQ purposes
* @enable_state: Enable state tracking
* @vblank_refcount: Reference count of vblank request
@@ -201,6 +202,7 @@ struct sde_encoder_phys {
enum sde_enc_split_role split_role;
enum sde_intf_mode intf_mode;
enum sde_intf intf_idx;
+ enum sde_csc_type enc_cdm_csc;
spinlock_t *enc_spinlock;
enum sde_enc_enable_state enable_state;
atomic_t vblank_refcount;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 78a8b732b0de..d58c06de1684 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -281,23 +281,40 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx)
{
struct sde_encoder_phys_vid *vid_enc = arg;
struct sde_encoder_phys *phys_enc;
+ struct sde_hw_ctl *hw_ctl;
unsigned long lock_flags;
- int new_cnt;
+ u32 flush_register = 0;
+ int new_cnt = -1, old_cnt = -1;
if (!vid_enc)
return;
phys_enc = &vid_enc->base;
+ hw_ctl = phys_enc->hw_ctl;
+
if (phys_enc->parent_ops.handle_vblank_virt)
phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
phys_enc);
+ old_cnt = atomic_read(&phys_enc->pending_kickoff_cnt);
+
+ /*
+ * only decrement the pending flush count if we've actually flushed
+ * hardware. due to sw irq latency, vblank may have already happened
+ * so we need to double-check with hw that it accepted the flush bits
+ */
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
- new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
- SDE_EVT32_IRQ(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0,
- new_cnt);
+ if (hw_ctl && hw_ctl->ops.get_flush_register)
+ flush_register = hw_ctl->ops.get_flush_register(hw_ctl);
+
+ if (flush_register == 0)
+ new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt,
+ -1, 0);
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
+ SDE_EVT32_IRQ(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0,
+ old_cnt, new_cnt, flush_register);
+
/* Signal any waiting atomic commit thread */
wake_up_all(&phys_enc->pending_kickoff_wq);
}
@@ -593,22 +610,33 @@ static void sde_encoder_phys_vid_get_hw_resources(
struct drm_connector_state *conn_state)
{
struct sde_encoder_phys_vid *vid_enc;
+ struct sde_mdss_cfg *vid_catalog;
if (!phys_enc || !hw_res) {
SDE_ERROR("invalid arg(s), enc %d hw_res %d conn_state %d\n",
- phys_enc != 0, hw_res != 0, conn_state != 0);
+ phys_enc != NULL, hw_res != NULL, conn_state != NULL);
return;
}
+ vid_catalog = phys_enc->sde_kms->catalog;
vid_enc = to_sde_encoder_phys_vid(phys_enc);
- if (!vid_enc->hw_intf) {
- SDE_ERROR("invalid arg(s), hw_intf\n");
+ if (!vid_enc->hw_intf || !vid_catalog) {
+ SDE_ERROR("invalid arg(s), hw_intf %d vid_catalog %d\n",
+ vid_enc->hw_intf != NULL, vid_catalog != NULL);
return;
}
SDE_DEBUG_VIDENC(vid_enc, "\n");
+ if (vid_enc->hw_intf->idx > INTF_MAX) {
+ SDE_ERROR("invalid arg(s), idx %d\n",
+ vid_enc->hw_intf->idx);
+ return;
+ }
hw_res->intfs[vid_enc->hw_intf->idx - INTF_0] = INTF_MODE_VIDEO;
- hw_res->needs_cdm = true;
+
+ if (vid_catalog->intf[vid_enc->hw_intf->idx - INTF_0].type
+ == INTF_HDMI)
+ hw_res->needs_cdm = true;
SDE_DEBUG_DRIVER("[vid] needs_cdm=%d\n", hw_res->needs_cdm);
}
@@ -689,6 +717,35 @@ static int sde_encoder_phys_vid_wait_for_commit_done(
return ret;
}
+static void sde_encoder_phys_vid_prepare_for_kickoff(
+ struct sde_encoder_phys *phys_enc)
+{
+ struct sde_encoder_phys_vid *vid_enc;
+ struct sde_hw_ctl *ctl;
+ int rc;
+
+ if (!phys_enc) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+ vid_enc = to_sde_encoder_phys_vid(phys_enc);
+
+ ctl = phys_enc->hw_ctl;
+ if (!ctl || !ctl->ops.wait_reset_status)
+ return;
+
+ /*
+ * hw supports hardware initiated ctl reset, so before we kickoff a new
+ * frame, need to check and wait for hw initiated ctl reset completion
+ */
+ rc = ctl->ops.wait_reset_status(ctl);
+ if (rc) {
+ SDE_ERROR_VIDENC(vid_enc, "ctl %d reset failure: %d\n",
+ ctl->idx, rc);
+ SDE_DBG_DUMP("panic");
+ }
+}
+
static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
{
struct msm_drm_private *priv;
@@ -821,6 +878,7 @@ static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops)
ops->get_hw_resources = sde_encoder_phys_vid_get_hw_resources;
ops->control_vblank_irq = sde_encoder_phys_vid_control_vblank_irq;
ops->wait_for_commit_done = sde_encoder_phys_vid_wait_for_commit_done;
+ ops->prepare_for_kickoff = sde_encoder_phys_vid_prepare_for_kickoff;
ops->handle_post_kickoff = sde_encoder_phys_vid_handle_post_kickoff;
ops->needs_single_flush = sde_encoder_phys_vid_needs_single_flush;
ops->setup_misr = sde_encoder_phys_vid_setup_misr;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 635c2e4e954b..ed9a6ea37397 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -27,11 +27,11 @@
/**
* Max hardware block in certain hardware. For ex: sspp pipes
- * can have QSEED, pcc, igc, pa, csc, etc. This count is max
- * 12 based on software design. It should be increased if any of the
+ * can have QSEED, pcc, igc, pa, csc, qos entries, etc. This count is
+ * 64 based on software design. It should be increased if any of the
* hardware block has more subblocks.
*/
-#define MAX_SDE_HW_BLK 12
+#define MAX_SDE_HW_BLK 64
/* each entry will have register address and bit offset in that register */
#define MAX_BIT_OFFSET 2
@@ -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},
};
@@ -440,8 +444,16 @@ static uint32_t _sde_copy_formats(
static int _parse_dt_u32_handler(struct device_node *np,
char *prop_name, u32 *offsets, int len, bool mandatory)
{
- int rc = of_property_read_u32_array(np, prop_name, offsets, len);
+ int rc = -EINVAL;
+ if (len > MAX_SDE_HW_BLK) {
+ SDE_ERROR(
+ "prop: %s tries out of bound access for u32 array read len: %d\n",
+ prop_name, len);
+ return -E2BIG;
+ }
+
+ rc = of_property_read_u32_array(np, prop_name, offsets, len);
if (rc && mandatory)
SDE_ERROR("mandatory prop: %s u32 array read len:%d\n",
prop_name, len);
@@ -463,6 +475,14 @@ static int _parse_dt_bit_offset(struct device_node *np,
if (arr) {
len /= sizeof(u32);
len &= ~0x1;
+
+ if (len > (MAX_SDE_HW_BLK * MAX_BIT_OFFSET)) {
+ SDE_ERROR(
+ "prop: %s len: %d will lead to out of bound access\n",
+ prop_name, len / MAX_BIT_OFFSET);
+ return -E2BIG;
+ }
+
for (i = 0, j = 0; i < len; j++) {
PROP_BITVALUE_ACCESS(prop_value, prop_index, j, 0) =
be32_to_cpu(arr[i]);
@@ -497,8 +517,8 @@ static int _validate_dt_entry(struct device_node *np,
sde_prop[0].prop_name);
if ((*off_count > MAX_BLOCKS) || (*off_count < 0)) {
if (sde_prop[0].is_mandatory) {
- SDE_ERROR("invalid hw offset prop name:%s\"\
- count: %d\n",
+ SDE_ERROR(
+ "invalid hw offset prop name:%s count: %d\n",
sde_prop[0].prop_name, *off_count);
rc = -EINVAL;
}
@@ -541,8 +561,9 @@ static int _validate_dt_entry(struct device_node *np,
sde_prop[i].type);
break;
}
- SDE_DEBUG("prop id:%d prop name:%s prop type:%d \"\
- prop_count:%d\n", i, sde_prop[i].prop_name,
+ SDE_DEBUG(
+ "prop id:%d prop name:%s prop type:%d prop_count:%d\n",
+ i, sde_prop[i].prop_name,
sde_prop[i].type, prop_count[i]);
if (rc && sde_prop[i].is_mandatory &&
@@ -560,14 +581,16 @@ static int _validate_dt_entry(struct device_node *np,
if (off_count && (prop_count[i] != *off_count) &&
sde_prop[i].is_mandatory) {
- SDE_ERROR("prop:%s count:%d is different compared to \"\
- offset array:%d\n", sde_prop[i].prop_name,
+ SDE_ERROR(
+ "prop:%s count:%d is different compared to offset array:%d\n",
+ sde_prop[i].prop_name,
prop_count[i], *off_count);
rc = -EINVAL;
goto end;
} else if (off_count && prop_count[i] != *off_count) {
- SDE_DEBUG("prop:%s count:%d is different compared to \"\
- offset array:%d\n", sde_prop[i].prop_name,
+ SDE_DEBUG(
+ "prop:%s count:%d is different compared to offset array:%d\n",
+ sde_prop[i].prop_name,
prop_count[i], *off_count);
rc = 0;
prop_count[i] = 0;
@@ -603,8 +626,9 @@ static int _read_dt_entry(struct device_node *np,
case PROP_TYPE_U32:
rc = of_property_read_u32(np, sde_prop[i].prop_name,
&PROP_VALUE_ACCESS(prop_value, i, 0));
- SDE_DEBUG("prop id:%d prop name:%s prop type:%d \"\
- value:0x%x\n", i, sde_prop[i].prop_name,
+ SDE_DEBUG(
+ "prop id:%d prop name:%s prop type:%d value:0x%x\n",
+ i, sde_prop[i].prop_name,
sde_prop[i].type,
PROP_VALUE_ACCESS(prop_value, i, 0));
if (rc)
@@ -614,8 +638,9 @@ static int _read_dt_entry(struct device_node *np,
PROP_VALUE_ACCESS(prop_value, i, 0) =
of_property_read_bool(np,
sde_prop[i].prop_name);
- SDE_DEBUG("prop id:%d prop name:%s prop type:%d \"\
- value:0x%x\n", i, sde_prop[i].prop_name,
+ SDE_DEBUG(
+ "prop id:%d prop name:%s prop type:%d value:0x%x\n",
+ i, sde_prop[i].prop_name,
sde_prop[i].type,
PROP_VALUE_ACCESS(prop_value, i, 0));
break;
@@ -624,8 +649,9 @@ static int _read_dt_entry(struct device_node *np,
&PROP_VALUE_ACCESS(prop_value, i, 0),
prop_count[i], sde_prop[i].is_mandatory);
if (rc && sde_prop[i].is_mandatory) {
- SDE_ERROR("%s prop validation success but \"\
- read failed\n", sde_prop[i].prop_name);
+ SDE_ERROR(
+ "%s prop validation success but read failed\n",
+ sde_prop[i].prop_name);
prop_exists[i] = false;
goto end;
} else {
@@ -647,19 +673,21 @@ static int _read_dt_entry(struct device_node *np,
prop_value, i, prop_count[i],
sde_prop[i].is_mandatory);
if (rc && sde_prop[i].is_mandatory) {
- SDE_ERROR("%s prop validation success but \"\
- read failed\n", sde_prop[i].prop_name);
+ SDE_ERROR(
+ "%s prop validation success but read failed\n",
+ sde_prop[i].prop_name);
prop_exists[i] = false;
goto end;
} else {
if (rc)
prop_exists[i] = false;
- SDE_DEBUG("prop id:%d prop name:%s prop \"\
- type:%d", i, sde_prop[i].prop_name,
+ SDE_DEBUG(
+ "prop id:%d prop name:%s prop type:%d",
+ i, sde_prop[i].prop_name,
sde_prop[i].type);
for (j = 0; j < prop_count[i]; j++)
- SDE_DEBUG(" count[%d]: bit:0x%x \"\
- off:0x%x \n", j,
+ SDE_DEBUG(
+ "count[%d]: bit:0x%x off:0x%x\n", j,
PROP_BITVALUE_ACCESS(prop_value,
i, j, 0),
PROP_BITVALUE_ACCESS(prop_value,
@@ -689,7 +717,10 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
{
sblk->maxupscale = MAX_SSPP_UPSCALE;
sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
+ sblk->format_list = plane_formats_yuv;
sspp->id = SSPP_VIG0 + *vig_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
+ sspp->id - SSPP_VIG0);
sspp->clk_ctrl = SDE_CLK_CTRL_VIG0 + *vig_count;
sspp->type = SSPP_TYPE_VIG;
set_bit(SDE_SSPP_QOS, &sspp->features);
@@ -703,14 +734,24 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED2;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_QSEED_OFF, 0);
- } else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) {
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ VIG_QSEED_LEN, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id - SSPP_VIG0);
+ } else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) {
set_bit(SDE_SSPP_SCALER_QSEED3, &sspp->features);
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_QSEED_OFF, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ VIG_QSEED_LEN, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id - SSPP_VIG0);
}
sblk->csc_blk.id = SDE_SSPP_CSC;
+ snprintf(sblk->csc_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_csc%u", sspp->id - SSPP_VIG0);
if (sde_cfg->csc_type == SDE_SSPP_CSC) {
set_bit(SDE_SSPP_CSC, &sspp->features);
sblk->csc_blk.base = PROP_VALUE_ACCESS(prop_value,
@@ -722,6 +763,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
}
sblk->hsic_blk.id = SDE_SSPP_HSIC;
+ snprintf(sblk->hsic_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_hsic%u", sspp->id - SSPP_VIG0);
if (prop_exists[VIG_HSIC_PROP]) {
sblk->hsic_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_HSIC_PROP, 0);
@@ -732,6 +775,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
}
sblk->memcolor_blk.id = SDE_SSPP_MEMCOLOR;
+ snprintf(sblk->memcolor_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_memcolor%u", sspp->id - SSPP_VIG0);
if (prop_exists[VIG_MEMCOLOR_PROP]) {
sblk->memcolor_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_MEMCOLOR_PROP, 0);
@@ -742,6 +787,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
}
sblk->pcc_blk.id = SDE_SSPP_PCC;
+ snprintf(sblk->pcc_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_pcc%u", sspp->id - SSPP_VIG0);
if (prop_exists[VIG_PCC_PROP]) {
sblk->pcc_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_PCC_PROP, 0);
@@ -759,7 +806,10 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg,
{
sblk->maxupscale = MAX_SSPP_UPSCALE;
sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
+ sblk->format_list = plane_formats;
sspp->id = SSPP_RGB0 + *rgb_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
+ sspp->id - SSPP_VIG0);
sspp->clk_ctrl = SDE_CLK_CTRL_RGB0 + *rgb_count;
sspp->type = SSPP_TYPE_RGB;
set_bit(SDE_SSPP_QOS, &sspp->features);
@@ -773,11 +823,19 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg,
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED2;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
RGB_SCALER_OFF, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ RGB_SCALER_LEN, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id - SSPP_VIG0);
} else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) {
set_bit(SDE_SSPP_SCALER_RGB, &sspp->features);
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
- RGB_SCALER_OFF, 0);
+ RGB_SCALER_LEN, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ SSPP_SCALE_SIZE, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id - SSPP_VIG0);
}
sblk->pcc_blk.id = SDE_SSPP_PCC;
@@ -799,7 +857,10 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg,
set_bit(SDE_SSPP_CURSOR, &sspp->features);
sblk->maxupscale = SSPP_UNITY_SCALE;
sblk->maxdwnscale = SSPP_UNITY_SCALE;
+ sblk->format_list = cursor_formats;
sspp->id = SSPP_CURSOR0 + *cursor_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
+ sspp->id - SSPP_VIG0);
sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count;
sspp->type = SSPP_TYPE_CURSOR;
(*cursor_count)++;
@@ -812,9 +873,12 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg,
{
sblk->maxupscale = SSPP_UNITY_SCALE;
sblk->maxdwnscale = SSPP_UNITY_SCALE;
+ sblk->format_list = plane_formats;
sspp->id = SSPP_DMA0 + *dma_count;
sspp->clk_ctrl = SDE_CLK_CTRL_DMA0 + *dma_count;
sspp->type = SSPP_TYPE_DMA;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
+ sspp->id - SSPP_VIG0);
set_bit(SDE_SSPP_QOS, &sspp->features);
(*dma_count)++;
snprintf(sspp->name, sizeof(sspp->name), "dma%d", *dma_count-1);
@@ -913,6 +977,7 @@ static int sde_sspp_parse_dt(struct device_node *np,
sspp->sblk = sblk;
sspp->base = PROP_VALUE_ACCESS(prop_value, SSPP_OFF, i);
+ sspp->len = PROP_VALUE_ACCESS(prop_value, SSPP_SIZE, 0);
sblk->maxlinewidth = sde_cfg->max_sspp_linewidth;
set_bit(SDE_SSPP_SRC, &sspp->features);
@@ -940,6 +1005,16 @@ 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);
+
+ if (sspp->clk_ctrl >= SDE_CLK_CTRL_MAX) {
+ SDE_ERROR("%s: invalid clk ctrl: %d\n",
+ sblk->src_blk.name, sspp->clk_ctrl);
+ rc = -EINVAL;
+ goto end;
+ }
+
sblk->maxhdeciexp = MAX_HORZ_DECIMATION;
sblk->maxvdeciexp = MAX_VERT_DECIMATION;
@@ -1029,7 +1104,10 @@ static int sde_ctl_parse_dt(struct device_node *np,
for (i = 0; i < off_count; i++) {
ctl = sde_cfg->ctl + i;
ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i);
+ ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0);
ctl->id = CTL_0 + i;
+ snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u",
+ ctl->id - CTL_0);
if (i < MAX_SPLIT_DISPLAY_CTL)
set_bit(SDE_CTL_SPLIT_DISPLAY, &ctl->features);
@@ -1121,6 +1199,9 @@ static int sde_mixer_parse_dt(struct device_node *np,
mixer->base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i);
mixer->len = PROP_VALUE_ACCESS(prop_value, MIXER_LEN, 0);
mixer->id = LM_0 + i;
+ snprintf(mixer->name, SDE_HW_BLK_NAME_LEN, "lm_%u",
+ mixer->id - LM_0);
+
if (!prop_exists[MIXER_LEN])
mixer->len = DEFAULT_SDE_HW_BLOCK_LEN;
@@ -1207,6 +1288,9 @@ static int sde_intf_parse_dt(struct device_node *np,
intf->base = PROP_VALUE_ACCESS(prop_value, INTF_OFF, i);
intf->len = PROP_VALUE_ACCESS(prop_value, INTF_LEN, 0);
intf->id = INTF_0 + i;
+ snprintf(intf->name, SDE_HW_BLK_NAME_LEN, "intf_%u",
+ intf->id - INTF_0);
+
if (!prop_exists[INTF_LEN])
intf->len = DEFAULT_SDE_HW_BLOCK_LEN;
@@ -1286,11 +1370,22 @@ static int sde_wb_parse_dt(struct device_node *np,
wb->base = PROP_VALUE_ACCESS(prop_value, WB_OFF, i);
wb->id = WB_0 + PROP_VALUE_ACCESS(prop_value, WB_ID, i);
+ snprintf(wb->name, SDE_HW_BLK_NAME_LEN, "wb_%u",
+ wb->id - WB_0);
wb->clk_ctrl = SDE_CLK_CTRL_WB0 +
PROP_VALUE_ACCESS(prop_value, WB_ID, i);
wb->xin_id = PROP_VALUE_ACCESS(prop_value, WB_XIN_ID, i);
wb->vbif_idx = VBIF_NRT;
+
+ if (wb->clk_ctrl >= SDE_CLK_CTRL_MAX) {
+ SDE_ERROR("%s: invalid clk ctrl: %d\n",
+ wb->name, wb->clk_ctrl);
+ rc = -EINVAL;
+ goto end;
+ }
+
wb->len = PROP_VALUE_ACCESS(prop_value, WB_LEN, 0);
+ wb->format_list = wb2_formats;
if (!prop_exists[WB_LEN])
wb->len = DEFAULT_SDE_HW_BLOCK_LEN;
sblk->maxlinewidth = sde_cfg->max_wb_linewidth;
@@ -1510,7 +1605,10 @@ static int sde_dspp_parse_dt(struct device_node *np,
for (i = 0; i < off_count; i++) {
dspp = sde_cfg->dspp + i;
dspp->base = PROP_VALUE_ACCESS(prop_value, DSPP_OFF, i);
+ dspp->len = PROP_VALUE_ACCESS(prop_value, DSPP_SIZE, 0);
dspp->id = DSPP_0 + i;
+ snprintf(dspp->name, SDE_HW_BLK_NAME_LEN, "dspp_%u",
+ dspp->id - DSPP_0);
sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
if (!sblk) {
@@ -1580,6 +1678,8 @@ static int sde_cdm_parse_dt(struct device_node *np,
cdm = sde_cfg->cdm + i;
cdm->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i);
cdm->id = CDM_0 + i;
+ snprintf(cdm->name, SDE_HW_BLK_NAME_LEN, "cdm_%u",
+ cdm->id - CDM_0);
cdm->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0);
/* intf3 and wb2 for cdm block */
@@ -1645,6 +1745,8 @@ static int sde_vbif_parse_dt(struct device_node *np,
vbif->base = PROP_VALUE_ACCESS(prop_value, VBIF_OFF, i);
vbif->len = vbif_len;
vbif->id = VBIF_0 + PROP_VALUE_ACCESS(prop_value, VBIF_ID, i);
+ snprintf(vbif->name, SDE_HW_BLK_NAME_LEN, "vbif_%u",
+ vbif->id - VBIF_0);
SDE_DEBUG("vbif:%d\n", vbif->id - VBIF_0);
@@ -1772,15 +1874,21 @@ static int sde_pp_parse_dt(struct device_node *np,
pp->base = PROP_VALUE_ACCESS(prop_value, PP_OFF, i);
pp->id = PINGPONG_0 + i;
+ snprintf(pp->name, SDE_HW_BLK_NAME_LEN, "pingpong_%u",
+ pp->id - PINGPONG_0);
pp->len = PROP_VALUE_ACCESS(prop_value, PP_LEN, 0);
sblk->te.base = PROP_VALUE_ACCESS(prop_value, TE_OFF, i);
sblk->te.id = SDE_PINGPONG_TE;
+ snprintf(sblk->te.name, SDE_HW_BLK_NAME_LEN, "te_%u",
+ pp->id - PINGPONG_0);
set_bit(SDE_PINGPONG_TE, &pp->features);
sblk->te2.base = PROP_VALUE_ACCESS(prop_value, TE2_OFF, i);
if (sblk->te2.base) {
sblk->te2.id = SDE_PINGPONG_TE2;
+ snprintf(sblk->te2.name, SDE_HW_BLK_NAME_LEN, "te2_%u",
+ pp->id - PINGPONG_0);
set_bit(SDE_PINGPONG_TE2, &pp->features);
set_bit(SDE_PINGPONG_SPLIT, &pp->features);
}
@@ -1791,6 +1899,8 @@ static int sde_pp_parse_dt(struct device_node *np,
sblk->dsc.base = PROP_VALUE_ACCESS(prop_value, DSC_OFF, i);
if (sblk->dsc.base) {
sblk->dsc.id = SDE_PINGPONG_DSC;
+ snprintf(sblk->dsc.name, SDE_HW_BLK_NAME_LEN, "dsc_%u",
+ sblk->dsc.id - PINGPONG_0);
set_bit(SDE_PINGPONG_DSC, &pp->features);
}
}
@@ -1921,9 +2031,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])
@@ -1992,7 +2106,7 @@ static int sde_perf_parse_dt(struct device_node *np,
goto end;
}
- prop_value = kzalloc(SDE_PROP_MAX *
+ prop_value = kzalloc(PERF_PROP_MAX *
sizeof(struct sde_prop_value), GFP_KERNEL);
if (!prop_value) {
rc = -ENOMEM;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index 73bb77b7afa6..81e6bfe6defe 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -44,10 +44,12 @@
#define SDE_HW_VER_172 SDE_HW_VER(1, 7, 2) /* 8996 v3.0 */
#define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */
#define SDE_HW_VER_301 SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */
-#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* msmskunk v1.0 */
+#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* sdm845 v1.0 */
#define IS_MSMSKUNK_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400)
+#define SDE_HW_BLK_NAME_LEN 16
+
#define MAX_IMG_WIDTH 0x3fff
#define MAX_IMG_HEIGHT 0x3fff
@@ -58,8 +60,6 @@
#define SDE_COLOR_PROCESS_MAJOR(version) (((version) & 0xFFFF0000) >> 16)
#define SDE_COLOR_PROCESS_MINOR(version) ((version) & 0xFFFF)
-#define SSPP_NAME_SIZE 12
-
/**
* MDP TOP BLOCK features
* @SDE_MDP_PANIC_PER_PIPE Panic configuration needs to be be done per pipe
@@ -236,12 +236,14 @@ enum {
/**
* MACRO SDE_HW_BLK_INFO - information of HW blocks inside SDE
+ * @name: string name for debug purposes
* @id: enum identifying this block
* @base: register base offset to mdss
* @len: length of hardware block
* @features bit mask identifying sub-blocks/features
*/
#define SDE_HW_BLK_INFO \
+ char name[SDE_HW_BLK_NAME_LEN]; \
u32 id; \
u32 base; \
u32 len; \
@@ -249,12 +251,14 @@ enum {
/**
* MACRO SDE_HW_SUBBLK_INFO - information of HW sub-block inside SDE
+ * @name: string name for debug purposes
* @id: enum identifying this sub-block
* @base: offset of this sub-block relative to the block
* offset
* @len register block length of this sub-block
*/
#define SDE_HW_SUBBLK_INFO \
+ char name[SDE_HW_BLK_NAME_LEN]; \
u32 id; \
u32 base; \
u32 len
@@ -458,7 +462,6 @@ struct sde_ctl_cfg {
* @sblk: SSPP sub-blocks information
* @xin_id: bus client identifier
* @clk_ctrl clock control identifier
- * @name source pipe name
* @type sspp type identifier
*/
struct sde_sspp_cfg {
@@ -466,7 +469,6 @@ struct sde_sspp_cfg {
const struct sde_sspp_sub_blks *sblk;
u32 xin_id;
enum sde_clk_ctrl_type clk_ctrl;
- char name[SSPP_NAME_SIZE];
u32 type;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index 9ec81c227e60..da04be4e9719 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -14,6 +14,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_cdm.h"
+#include "sde_dbg.h"
#define CDM_CSC_10_OPMODE 0x000
#define CDM_CSC_10_BASE 0x004
@@ -295,6 +296,9 @@ struct sde_hw_cdm *sde_hw_cdm_init(enum sde_cdm idx,
*/
sde_hw_cdm_setup_csc_10bit(c, &rgb2yuv_cfg);
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 56d9f2a4a9b8..270e79a774b2 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include "sde_hwio.h"
#include "sde_hw_ctl.h"
+#include "sde_dbg.h"
#define CTL_LAYER(lm) \
(((lm) == LM_5) ? (0x024) : (((lm) - LM_0) * 0x004))
@@ -39,6 +40,7 @@ static struct sde_ctl_cfg *_ctl_offset(enum sde_ctl ctl,
if (ctl == m->ctl[i].id) {
b->base_off = addr;
b->blk_off = m->ctl[i].base;
+ b->length = m->ctl[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_CTL;
return &m->ctl[i];
@@ -92,6 +94,12 @@ static inline void sde_hw_ctl_trigger_flush(struct sde_hw_ctl *ctx)
SDE_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
}
+static inline u32 sde_hw_ctl_get_flush_register(struct sde_hw_ctl *ctx)
+{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+
+ return SDE_REG_READ(c, CTL_FLUSH);
+}
static inline uint32_t sde_hw_ctl_get_bitmask_sspp(struct sde_hw_ctl *ctx,
enum sde_sspp sspp)
@@ -247,23 +255,58 @@ static inline int sde_hw_ctl_get_bitmask_cdm(struct sde_hw_ctl *ctx,
return 0;
}
+static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 count)
+{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+ u32 status;
+
+ /* protect to do at least one iteration */
+ if (!count)
+ count = 1;
+
+ /*
+ * it takes around 30us to have mdp finish resetting its ctl path
+ * poll every 50us so that reset should be completed at 1st poll
+ */
+ do {
+ status = SDE_REG_READ(c, CTL_SW_RESET);
+ status &= 0x01;
+ if (status)
+ usleep_range(20, 50);
+ } while (status && --count > 0);
+
+ return status;
+}
+
static int sde_hw_ctl_reset_control(struct sde_hw_ctl *ctx)
{
struct sde_hw_blk_reg_map *c = &ctx->hw;
- int count = SDE_REG_RESET_TIMEOUT_COUNT;
- int reset;
+ pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx);
SDE_REG_WRITE(c, CTL_SW_RESET, 0x1);
+ if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT))
+ return -EINVAL;
- for (; count > 0; count--) {
- /* insert small delay to avoid spinning the cpu while waiting */
- usleep_range(20, 50);
- reset = SDE_REG_READ(c, CTL_SW_RESET);
- if (reset == 0)
- return 0;
+ return 0;
+}
+
+static int sde_hw_ctl_wait_reset_status(struct sde_hw_ctl *ctx)
+{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+ u32 status;
+
+ status = SDE_REG_READ(c, CTL_SW_RESET);
+ status &= 0x01;
+ if (!status)
+ return 0;
+
+ pr_debug("hw ctl reset is set for ctl:%d\n", ctx->idx);
+ if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT)) {
+ pr_err("hw recovery is not complete for ctl:%d\n", ctx->idx);
+ return -EINVAL;
}
- return -EINVAL;
+ return 0;
}
static void sde_hw_ctl_clear_all_blendstages(struct sde_hw_ctl *ctx)
@@ -415,9 +458,11 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops,
ops->update_pending_flush = sde_hw_ctl_update_pending_flush;
ops->get_pending_flush = sde_hw_ctl_get_pending_flush;
ops->trigger_flush = sde_hw_ctl_trigger_flush;
+ ops->get_flush_register = sde_hw_ctl_get_flush_register;
ops->trigger_start = sde_hw_ctl_trigger_start;
ops->setup_intf_cfg = sde_hw_ctl_intf_cfg;
ops->reset = sde_hw_ctl_reset_control;
+ ops->wait_reset_status = sde_hw_ctl_wait_reset_status;
ops->clear_all_blendstages = sde_hw_ctl_clear_all_blendstages;
ops->setup_blendstage = sde_hw_ctl_setup_blendstage;
ops->get_bitmask_sspp = sde_hw_ctl_get_bitmask_sspp;
@@ -452,6 +497,9 @@ struct sde_hw_ctl *sde_hw_ctl_init(enum sde_ctl idx,
c->mixer_count = m->mixer_count;
c->mixer_hw_caps = m->mixer;
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index 2fb7b377e51d..74dbde92639a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -94,6 +94,13 @@ struct sde_hw_ctl_ops {
void (*trigger_flush)(struct sde_hw_ctl *ctx);
/**
+ * Read the value of the flush register
+ * @ctx : ctl path ctx pointer
+ * @Return: value of the ctl flush register.
+ */
+ u32 (*get_flush_register)(struct sde_hw_ctl *ctx);
+
+ /**
* Setup ctl_path interface config
* @ctx
* @cfg : interface config structure pointer
@@ -103,6 +110,17 @@ struct sde_hw_ctl_ops {
int (*reset)(struct sde_hw_ctl *c);
+ /*
+ * wait_reset_status - checks ctl reset status
+ * @ctx : ctl path ctx pointer
+ *
+ * This function checks the ctl reset status bit.
+ * If the reset bit is set, it keeps polling the status till the hw
+ * reset is complete.
+ * Returns: 0 on success or -error if reset incomplete within interval
+ */
+ int (*wait_reset_status)(struct sde_hw_ctl *ctx);
+
uint32_t (*get_bitmask_sspp)(struct sde_hw_ctl *ctx,
enum sde_sspp blk);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index d6250b07b4f0..2fd879a0030d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
#include "sde_hw_catalog.h"
#include "sde_hw_dspp.h"
#include "sde_hw_color_processing.h"
+#include "sde_dbg.h"
static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
struct sde_mdss_cfg *m,
@@ -27,6 +28,7 @@ static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
if (dspp == m->dspp[i].id) {
b->base_off = addr;
b->blk_off = m->dspp[i].base;
+ b->length = m->dspp[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_DSPP;
return &m->dspp[i];
@@ -111,6 +113,9 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx,
c->cap = cfg;
_setup_dspp_ops(c, c->cap->features);
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
index 042b0ee7909a..9e1b97800cb9 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_intf.h"
+#include "sde_dbg.h"
#define INTF_TIMING_ENGINE_EN 0x000
#define INTF_CONFIG 0x004
@@ -83,6 +84,7 @@ static struct sde_intf_cfg *_intf_offset(enum sde_intf intf,
(m->intf[i].type != INTF_NONE)) {
b->base_off = addr;
b->blk_off = m->intf[i].base;
+ b->length = m->intf[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_INTF;
return &m->intf[i];
@@ -324,9 +326,9 @@ struct sde_hw_intf *sde_hw_intf_init(enum sde_intf idx,
c->mdss = m;
_setup_intf_ops(&c->ops, c->cap->features);
- /*
- * Perform any default initialization for the intf
- */
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index 365b9b17715d..8b4e0901458f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,7 @@
#include "sde_hwio.h"
#include "sde_hw_lm.h"
#include "sde_hw_mdss.h"
+#include "sde_dbg.h"
#define LM_OP_MODE 0x00
#define LM_OUT_SIZE 0x04
@@ -37,6 +38,7 @@ static struct sde_lm_cfg *_lm_offset(enum sde_lm mixer,
if (mixer == m->mixer[i].id) {
b->base_off = addr;
b->blk_off = m->mixer[i].base;
+ b->length = m->mixer[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_LM;
return &m->mixer[i];
@@ -195,9 +197,9 @@ struct sde_hw_mixer *sde_hw_lm_init(enum sde_lm idx,
c->cap = cfg;
_setup_mixer_ops(m, &c->ops, c->cap->features);
- /*
- * Perform any default initialization for the sspp blocks
- */
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index 1edeff6a7aec..3d63d01a6d4e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -18,6 +18,8 @@
#include "msm_drv.h"
+#define SDE_DBG_NAME "sde"
+
#define SDE_NONE 0
#ifndef SDE_CSC_MATRIX_COEFF_SIZE
@@ -344,6 +346,9 @@ enum sde_3d_blend_mode {
enum sde_csc_type {
SDE_CSC_RGB2YUV_601L,
SDE_CSC_RGB2YUV_601FR,
+ SDE_CSC_RGB2YUV_709L,
+ SDE_CSC_RGB2YUV_2020L,
+ SDE_CSC_RGB2YUV_2020FR,
SDE_MAX_CSC
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
index 837edeeba4c6..8488d03af79a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_pingpong.h"
+#include "sde_dbg.h"
#define PP_TEAR_CHECK_EN 0x000
#define PP_SYNC_CONFIG_VSYNC 0x004
@@ -47,6 +48,7 @@ static struct sde_pingpong_cfg *_pingpong_offset(enum sde_pingpong pp,
if (pp == m->pingpong[i].id) {
b->base_off = addr;
b->blk_off = m->pingpong[i].base;
+ b->length = m->pingpong[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_PINGPONG;
return &m->pingpong[i];
@@ -159,6 +161,9 @@ struct sde_hw_pingpong *sde_hw_pingpong_init(enum sde_pingpong idx,
c->pingpong_hw_cap = cfg;
_setup_pingpong_ops(&c->ops, c->pingpong_hw_cap->features);
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index ea2890d776ae..be620aebf850 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -15,6 +15,7 @@
#include "sde_hw_lm.h"
#include "sde_hw_sspp.h"
#include "sde_hw_color_processing.h"
+#include "sde_dbg.h"
#define SDE_FETCH_CONFIG_RESET_VALUE 0x00000087
@@ -903,6 +904,7 @@ static struct sde_sspp_cfg *_sspp_offset(enum sde_sspp sspp,
if (sspp == catalog->sspp[i].id) {
b->base_off = addr;
b->blk_off = catalog->sspp[i].base;
+ b->length = catalog->sspp[i].len;
b->hwversion = catalog->hwversion;
b->log_mask = SDE_DBG_MASK_SSPP;
return &catalog->sspp[i];
@@ -917,26 +919,39 @@ struct sde_hw_pipe *sde_hw_sspp_init(enum sde_sspp idx,
void __iomem *addr,
struct sde_mdss_cfg *catalog)
{
- struct sde_hw_pipe *ctx;
+ struct sde_hw_pipe *hw_pipe;
struct sde_sspp_cfg *cfg;
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
+ hw_pipe = kzalloc(sizeof(*hw_pipe), GFP_KERNEL);
+ if (!hw_pipe)
return ERR_PTR(-ENOMEM);
- cfg = _sspp_offset(idx, addr, catalog, &ctx->hw);
+ cfg = _sspp_offset(idx, addr, catalog, &hw_pipe->hw);
if (IS_ERR_OR_NULL(cfg)) {
- kfree(ctx);
+ kfree(hw_pipe);
return ERR_PTR(-EINVAL);
}
/* Assign ops */
- ctx->idx = idx;
- ctx->cap = cfg;
- _setup_layer_ops(ctx, ctx->cap->features);
- ctx->highest_bank_bit = catalog->mdp[0].highest_bank_bit;
-
- return ctx;
+ hw_pipe->idx = idx;
+ hw_pipe->cap = cfg;
+ _setup_layer_ops(hw_pipe, hw_pipe->cap->features);
+ hw_pipe->highest_bank_bit = catalog->mdp[0].highest_bank_bit;
+
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name,
+ hw_pipe->hw.blk_off,
+ hw_pipe->hw.blk_off + hw_pipe->hw.length,
+ hw_pipe->hw.xin_id);
+
+ if (cfg->sblk->scaler_blk.len)
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME,
+ cfg->sblk->scaler_blk.name,
+ hw_pipe->hw.blk_off + cfg->sblk->scaler_blk.base,
+ hw_pipe->hw.blk_off + cfg->sblk->scaler_blk.base +
+ cfg->sblk->scaler_blk.len,
+ hw_pipe->hw.xin_id);
+
+ return hw_pipe;
}
void sde_hw_sspp_destroy(struct sde_hw_pipe *ctx)
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index d6d2e41ff5aa..218797e623a2 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_top.h"
+#include "sde_dbg.h"
#define SSPP_SPARE 0x28
@@ -225,6 +226,7 @@ static const struct sde_mdp_cfg *_top_offset(enum sde_mdp mdp,
if (mdp == m->mdp[i].id) {
b->base_off = addr;
b->blk_off = m->mdp[i].base;
+ b->length = m->mdp[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_TOP;
return &m->mdp[i];
@@ -258,9 +260,10 @@ struct sde_hw_mdp *sde_hw_mdptop_init(enum sde_mdp idx,
mdp->cap = cfg;
_setup_mdp_ops(&mdp->ops, mdp->cap->features);
- /*
- * Perform any default initialization for the intf
- */
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name,
+ mdp->hw.blk_off, mdp->hw.blk_off + mdp->hw.length,
+ mdp->hw.xin_id);
+ sde_dbg_set_sde_top_offset(mdp->hw.blk_off);
return mdp;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.h b/drivers/gpu/drm/msm/sde/sde_hw_util.h
index c38c22237a57..008b657966b6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_util.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_util.h
@@ -24,12 +24,14 @@
* @base_off: mdp register mapped offset
* @blk_off: pipe offset relative to mdss offset
* @length length of register block offset
+ * @xin_id xin id
* @hwversion mdss hw version number
*/
struct sde_hw_blk_reg_map {
void __iomem *base_off;
u32 blk_off;
u32 length;
+ u32 xin_id;
u32 hwversion;
u32 log_mask;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
index 76473fa879c5..048ec47d7c72 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_vbif.h"
+#include "sde_dbg.h"
#define VBIF_VERSION 0x0000
#define VBIF_CLK_FORCE_CTRL0 0x0008
@@ -123,6 +124,7 @@ static const struct sde_vbif_cfg *_top_offset(enum sde_vbif vbif,
if (vbif == m->vbif[i].id) {
b->base_off = addr;
b->blk_off = m->vbif[i].base;
+ b->length = m->vbif[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_VBIF;
return &m->vbif[i];
@@ -156,6 +158,8 @@ struct sde_hw_vbif *sde_hw_vbif_init(enum sde_vbif idx,
c->cap = cfg;
_setup_vbif_ops(&c->ops, c->cap->features);
+ /* no need to register sub-range in sde dbg, dump entire vbif io base */
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.c b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
index 426e9991a6b5..320b05f67669 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
#include "sde_hw_catalog.h"
#include "sde_hw_wb.h"
#include "sde_formats.h"
+#include "sde_dbg.h"
#define WB_DST_FORMAT 0x000
#define WB_DST_OP_MODE 0x004
@@ -57,6 +58,7 @@ static struct sde_wb_cfg *_wb_offset(enum sde_wb wb,
if (wb == m->wb[i].id) {
b->base_off = addr;
b->blk_off = m->wb[i].base;
+ b->length = m->wb[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_WB;
return &m->wb[i];
@@ -215,6 +217,9 @@ struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx,
c->highest_bank_bit = m->mdp[0].highest_bank_bit;
c->hw_mdp = hw_mdp;
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 031493aa42b8..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");
@@ -328,24 +328,12 @@ static int sde_debugfs_danger_init(struct sde_kms *sde_kms,
static int sde_kms_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
{
- struct sde_kms *sde_kms = to_sde_kms(kms);
- struct drm_device *dev = sde_kms->dev;
- struct msm_drm_private *priv = dev->dev_private;
-
- sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
-
return sde_crtc_vblank(crtc, true);
}
static void sde_kms_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
{
- struct sde_kms *sde_kms = to_sde_kms(kms);
- struct drm_device *dev = sde_kms->dev;
- struct msm_drm_private *priv = dev->dev_private;
-
sde_crtc_vblank(crtc, false);
-
- sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
}
static void sde_kms_prepare_commit(struct msm_kms *kms,
@@ -355,6 +343,9 @@ static void sde_kms_prepare_commit(struct msm_kms *kms,
struct drm_device *dev = sde_kms->dev;
struct msm_drm_private *priv = dev->dev_private;
+ if (sde_kms->splash_info.handoff)
+ sde_splash_clean_up_exit_lk(kms);
+
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
}
@@ -603,6 +594,8 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
.set_property = sde_hdmi_set_property,
.get_property = sde_hdmi_get_property,
.pre_kickoff = sde_hdmi_pre_kickoff,
+ .mode_needs_full_range = sde_hdmi_mode_needs_full_range,
+ .get_csc_type = sde_hdmi_get_csc_type
};
struct msm_display_info info = {0};
struct drm_encoder *encoder;
@@ -997,8 +990,15 @@ static void _sde_kms_hw_destroy(struct sde_kms *sde_kms,
sde_hw_catalog_deinit(sde_kms->catalog);
sde_kms->catalog = NULL;
+ if (sde_kms->splash_info.handoff) {
+ if (sde_kms->core_client)
+ sde_splash_destroy(&sde_kms->splash_info,
+ &priv->phandle, sde_kms->core_client);
+ }
+
if (sde_kms->core_client)
- sde_power_client_destroy(&priv->phandle, sde_kms->core_client);
+ sde_power_client_destroy(&priv->phandle,
+ sde_kms->core_client);
sde_kms->core_client = NULL;
if (sde_kms->vbif[VBIF_NRT])
@@ -1110,6 +1110,24 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms)
continue;
}
+ /* Attaching smmu means IOMMU HW starts to work immediately.
+ * However, display HW in LK is still accessing memory
+ * while the memory map is not done yet.
+ * So first set DOMAIN_ATTR_EARLY_MAP attribute 1 to bypass
+ * stage 1 translation in IOMMU HW.
+ */
+ if ((i == MSM_SMMU_DOMAIN_UNSECURE) &&
+ sde_kms->splash_info.handoff) {
+ ret = mmu->funcs->set_property(mmu,
+ DOMAIN_ATTR_EARLY_MAP,
+ &sde_kms->splash_info.handoff);
+ if (ret) {
+ SDE_ERROR("failed to set map att: %d\n", ret);
+ mmu->funcs->destroy(mmu);
+ goto fail;
+ }
+ }
+
aspace = msm_gem_smmu_address_space_create(sde_kms->dev->dev,
mmu, "sde");
if (IS_ERR(aspace)) {
@@ -1127,6 +1145,19 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms)
goto fail;
}
+ /*
+ * It's safe now to map the physical memory blcok LK accesses.
+ */
+ if ((i == MSM_SMMU_DOMAIN_UNSECURE) &&
+ sde_kms->splash_info.handoff) {
+ ret = sde_splash_smmu_map(sde_kms->dev, mmu,
+ &sde_kms->splash_info);
+ if (ret) {
+ SDE_ERROR("map rsv mem failed: %d\n", ret);
+ msm_gem_address_space_put(aspace);
+ goto fail;
+ }
+ }
}
return 0;
@@ -1136,11 +1167,50 @@ fail:
return ret;
}
+static void __iomem *_sde_kms_ioremap(struct platform_device *pdev,
+ const char *name, unsigned long *out_size)
+{
+ struct resource *res;
+ unsigned long size;
+ void __iomem *ptr;
+
+ if (out_size)
+ *out_size = 0;
+
+ if (name)
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ else
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ /* availability depends on platform */
+ SDE_DEBUG("failed to get memory resource: %s\n", name);
+ return NULL;
+ }
+
+ size = resource_size(res);
+
+ ptr = devm_ioremap_nocache(&pdev->dev, res->start, size);
+ if (!ptr) {
+ SDE_ERROR("failed to ioremap: %s\n", name);
+ return NULL;
+ }
+
+ SDE_DEBUG("IO:region %s %pK %08lx\n", name, ptr, size);
+
+ if (out_size)
+ *out_size = size;
+
+ return ptr;
+}
+
+
static int sde_kms_hw_init(struct msm_kms *kms)
{
struct sde_kms *sde_kms;
struct drm_device *dev;
struct msm_drm_private *priv;
+ struct sde_splash_info *sinfo;
int i, rc = -EINVAL;
if (!kms) {
@@ -1161,29 +1231,42 @@ static int sde_kms_hw_init(struct msm_kms *kms)
goto end;
}
- sde_kms->mmio = msm_ioremap(dev->platformdev, "mdp_phys", "SDE");
- if (IS_ERR(sde_kms->mmio)) {
- rc = PTR_ERR(sde_kms->mmio);
- SDE_ERROR("mdp register memory map failed: %d\n", rc);
- sde_kms->mmio = NULL;
+ sde_kms->mmio = _sde_kms_ioremap(dev->platformdev, "mdp_phys",
+ &sde_kms->mmio_len);
+ if (!sde_kms->mmio) {
+ SDE_ERROR("mdp register memory map failed\n");
goto error;
}
DRM_INFO("mapped mdp address space @%p\n", sde_kms->mmio);
- sde_kms->vbif[VBIF_RT] = msm_ioremap(dev->platformdev,
- "vbif_phys", "VBIF");
- if (IS_ERR(sde_kms->vbif[VBIF_RT])) {
- rc = PTR_ERR(sde_kms->vbif[VBIF_RT]);
- SDE_ERROR("vbif register memory map failed: %d\n", rc);
- sde_kms->vbif[VBIF_RT] = NULL;
+ rc = sde_dbg_reg_register_base(SDE_DBG_NAME, sde_kms->mmio,
+ sde_kms->mmio_len);
+ if (rc)
+ SDE_ERROR("dbg base register kms failed: %d\n", rc);
+
+ sde_kms->vbif[VBIF_RT] = _sde_kms_ioremap(dev->platformdev, "vbif_phys",
+ &sde_kms->vbif_len[VBIF_RT]);
+ if (!sde_kms->vbif[VBIF_RT]) {
+ SDE_ERROR("vbif register memory map failed\n");
goto error;
}
- sde_kms->vbif[VBIF_NRT] = msm_ioremap(dev->platformdev,
- "vbif_nrt_phys", "VBIF_NRT");
- if (IS_ERR(sde_kms->vbif[VBIF_NRT])) {
- sde_kms->vbif[VBIF_NRT] = NULL;
+ rc = sde_dbg_reg_register_base("vbif_rt", sde_kms->vbif[VBIF_RT],
+ sde_kms->vbif_len[VBIF_RT]);
+ if (rc)
+ SDE_ERROR("dbg base register vbif_rt failed: %d\n", rc);
+
+ sde_kms->vbif[VBIF_NRT] = _sde_kms_ioremap(dev->platformdev,
+ "vbif_nrt_phys", &sde_kms->vbif_len[VBIF_NRT]);
+ if (!sde_kms->vbif[VBIF_NRT]) {
SDE_DEBUG("VBIF NRT is not defined");
+ } else {
+ rc = sde_dbg_reg_register_base("vbif_nrt",
+ sde_kms->vbif[VBIF_NRT],
+ sde_kms->vbif_len[VBIF_NRT]);
+ if (rc)
+ SDE_ERROR("dbg base register vbif_nrt failed: %d\n",
+ rc);
}
sde_kms->core_client = sde_power_client_create(&priv->phandle, "core");
@@ -1213,6 +1296,8 @@ static int sde_kms_hw_init(struct msm_kms *kms)
goto power_error;
}
+ sde_dbg_init_dbg_buses(sde_kms->core_rev);
+
rc = sde_rm_init(&sde_kms->rm, sde_kms->catalog, sde_kms->mmio,
sde_kms->dev);
if (rc) {
@@ -1230,6 +1315,35 @@ static int sde_kms_hw_init(struct msm_kms *kms)
goto power_error;
}
+ /*
+ * Read the DISP_INTF_SEL register to check
+ * whether early display is enabled in LK.
+ */
+ rc = sde_splash_get_handoff_status(kms);
+ if (rc) {
+ SDE_ERROR("get early splash status failed: %d\n", rc);
+ goto power_error;
+ }
+
+ /*
+ * when LK has enabled early display, sde_splash_parse_dt and
+ * sde_splash_init must be called. The first function is to parse the
+ * mandatory memory node for splash function, and the second function
+ * will first do bandwidth voting job, because display hardware is now
+ * accessing AHB data bus, otherwise device reboot will happen, and then
+ * to check if the memory is reserved.
+ */
+ sinfo = &sde_kms->splash_info;
+ if (sinfo->handoff) {
+ rc = sde_splash_parse_dt(dev);
+ if (rc) {
+ SDE_ERROR("parse dt for splash info failed: %d\n", rc);
+ goto power_error;
+ }
+
+ sde_splash_init(&priv->phandle, kms);
+ }
+
for (i = 0; i < sde_kms->catalog->vbif_count; i++) {
u32 vbif_idx = sde_kms->catalog->vbif[i].id;
@@ -1304,7 +1418,10 @@ static int sde_kms_hw_init(struct msm_kms *kms)
*/
dev->mode_config.allow_fb_modifiers = true;
- sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
+ if (!sde_kms->splash_info.handoff)
+ sde_power_resource_enable(&priv->phandle,
+ sde_kms->core_client, false);
+
return 0;
drm_obj_init_err:
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index 44f6be959ac9..dee16d119d47 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -34,6 +34,7 @@
#include "sde_power_handle.h"
#include "sde_irq.h"
#include "sde_core_perf.h"
+#include "sde_splash.h"
#define DRMID(x) ((x) ? (x)->base.id : -1)
@@ -133,6 +134,7 @@ struct sde_kms {
/* io/register spaces: */
void __iomem *mmio, *vbif[VBIF_MAX];
+ unsigned long mmio_len, vbif_len[VBIF_MAX];
struct regulator *vdd;
struct regulator *mmagic;
@@ -157,6 +159,9 @@ struct sde_kms {
bool has_danger_ctrl;
void **hdmi_displays;
int hdmi_display_count;
+
+ /* splash handoff structure */
+ struct sde_splash_info splash_info;
};
struct vsync_info {
@@ -277,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
@@ -364,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/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 6fe1d1629d22..6e2ccfa8e428 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -34,6 +34,14 @@
#include "sde_plane.h"
#include "sde_color_processing.h"
+static bool suspend_blank = true;
+module_param(suspend_blank, bool, 0400);
+MODULE_PARM_DESC(suspend_blank,
+ "If set, active planes will force their outputs to black,\n"
+ "by temporarily enabling the color fill, when recovering\n"
+ "from a system resume instead of attempting to display the\n"
+ "last provided frame buffer.");
+
#define SDE_DEBUG_PLANE(pl, fmt, ...) SDE_DEBUG("plane%d " fmt,\
(pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
@@ -138,6 +146,7 @@ struct sde_plane {
struct sde_debugfs_regset32 debugfs_src;
struct sde_debugfs_regset32 debugfs_scaler;
struct sde_debugfs_regset32 debugfs_csc;
+ bool debugfs_default_scale;
};
#define to_sde_plane(x) container_of(x, struct sde_plane, base)
@@ -686,9 +695,20 @@ static int _sde_plane_setup_scaler3_lut(struct sde_phy_plane *pp,
struct sde_plane_state *pstate)
{
struct sde_plane *psde = pp->sde_plane;
- struct sde_hw_scaler3_cfg *cfg = pp->scaler3_cfg;
+ struct sde_hw_scaler3_cfg *cfg;
int ret = 0;
+ if (!pp || !pp->scaler3_cfg) {
+ SDE_ERROR("invalid args\n");
+ return -EINVAL;
+ } else if (!pstate) {
+ /* pstate is expected to be null on forced color fill */
+ SDE_DEBUG("null pstate\n");
+ return -EINVAL;
+ }
+
+ cfg = pp->scaler3_cfg;
+
cfg->dir_lut = msm_property_get_blob(
&psde->property_info,
pstate->property_blobs, &cfg->dir_len,
@@ -723,6 +743,7 @@ static void _sde_plane_setup_scaler3(struct sde_phy_plane *pp,
}
memset(scale_cfg, 0, sizeof(*scale_cfg));
+ memset(&pp->pixel_ext, 0, sizeof(struct sde_hw_pixel_ext));
decimated = DECIMATED_DIMENSION(src_w,
pp->pipe_cfg.horz_decimation);
@@ -1070,7 +1091,8 @@ static void _sde_plane_setup_scaler(struct sde_phy_plane *pp,
int error;
error = _sde_plane_setup_scaler3_lut(pp, pstate);
- if (error || !pp->pixel_ext_usr) {
+ if (error || !pp->pixel_ext_usr ||
+ psde->debugfs_default_scale) {
memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
/* calculate default config for QSEED3 */
_sde_plane_setup_scaler3(pp,
@@ -1081,7 +1103,8 @@ static void _sde_plane_setup_scaler(struct sde_phy_plane *pp,
pp->scaler3_cfg, fmt,
chroma_subsmpl_h, chroma_subsmpl_v);
}
- } else if (!pp->pixel_ext_usr) {
+ } else if (!pp->pixel_ext_usr || !pstate ||
+ psde->debugfs_default_scale) {
uint32_t deci_dim, i;
/* calculate default configuration for QSEED2 */
@@ -1701,8 +1724,8 @@ void sde_plane_flush(struct drm_plane *plane)
*/
list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
if (psde->is_error)
- /* force white frame with 0% alpha pipe output on error */
- _sde_plane_color_fill(pp, 0xFFFFFF, 0x0);
+ /* force white frame with 100% alpha pipe output on error */
+ _sde_plane_color_fill(pp, 0xFFFFFF, 0xFF);
else if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG)
/* force 100% alpha */
_sde_plane_color_fill(pp, pp->color_fill, 0xFF);
@@ -1711,6 +1734,10 @@ void sde_plane_flush(struct drm_plane *plane)
pp->pipe_hw->ops.setup_csc(pp->pipe_hw, pp->csc_ptr);
}
+ /* force black color fill during suspend */
+ if (msm_is_suspend_state(plane->dev) && suspend_blank)
+ _sde_plane_color_fill(pp, 0x0, 0x0);
+
/* flag h/w flush complete */
if (plane->state)
to_sde_plane_state(plane->state)->pending = false;
@@ -2582,6 +2609,10 @@ static void _sde_plane_init_debugfs(struct sde_plane *psde,
sde_debugfs_create_regset32("scaler_blk", S_IRUGO,
psde->debugfs_root,
&psde->debugfs_scaler);
+ debugfs_create_bool("default_scaling",
+ 0644,
+ psde->debugfs_root,
+ &psde->debugfs_default_scale);
sde_debugfs_setup_regset32(&psde->debugfs_csc,
sblk->csc_blk.base + cfg->base,
diff --git a/drivers/gpu/drm/msm/sde/sde_splash.c b/drivers/gpu/drm/msm/sde/sde_splash.c
new file mode 100644
index 000000000000..19e6406600cd
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_splash.c
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/of_address.h>
+#include <linux/debugfs.h>
+#include <linux/memblock.h>
+
+#include "msm_drv.h"
+#include "msm_mmu.h"
+#include "sde_kms.h"
+#include "sde_hw_mdss.h"
+#include "sde_hw_util.h"
+#include "sde_hw_intf.h"
+#include "sde_hw_catalog.h"
+#include "dsi_display.h"
+
+#define MDP_SSPP_TOP0_OFF 0x1000
+#define DISP_INTF_SEL 0x004
+#define SPLIT_DISPLAY_EN 0x2F4
+
+/* scratch registers */
+#define SCRATCH_REGISTER_0 0x014
+#define SCRATCH_REGISTER_1 0x018
+#define SCRATCH_REGISTER_2 0x01C
+
+#define SDE_LK_RUNNING_VALUE 0xC001CAFE
+#define SDE_LK_SHUT_DOWN_VALUE 0xDEADDEAD
+#define SDE_LK_EXIT_VALUE 0xDEADBEEF
+
+#define SDE_LK_EXIT_MAX_LOOP 20
+
+static DEFINE_MUTEX(sde_splash_lock);
+
+/*
+ * In order to free reseved memory from bootup, and we are not
+ * able to call the __init free functions, so we need to free
+ * this memory by ourselves using the free_reserved_page() function.
+ */
+static void _sde_splash_free_bootup_memory_to_system(phys_addr_t phys,
+ size_t size)
+{
+ unsigned long pfn_start, pfn_end, pfn_idx;
+
+ memblock_free(phys, size);
+
+ pfn_start = phys >> PAGE_SHIFT;
+ pfn_end = (phys + size) >> PAGE_SHIFT;
+
+ for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++)
+ free_reserved_page(pfn_to_page(pfn_idx));
+}
+
+static int _sde_splash_parse_dt_get_lk_pool_node(struct drm_device *dev,
+ struct sde_splash_info *sinfo)
+{
+ struct device_node *parent, *node;
+ struct resource r;
+ int ret = 0;
+
+ if (!sinfo)
+ return -EINVAL;
+
+ parent = of_find_node_by_path("/reserved-memory");
+ if (!parent)
+ return -EINVAL;
+
+ node = of_find_node_by_name(parent, "lk_pool");
+ if (!node) {
+ SDE_ERROR("mem reservation for lk_pool is not presented\n");
+ ret = -EINVAL;
+ goto parent_node_err;
+ }
+
+ /* find the mode */
+ if (of_address_to_resource(node, 0, &r)) {
+ ret = -EINVAL;
+ goto child_node_err;
+ }
+
+ sinfo->lk_pool_paddr = (dma_addr_t)r.start;
+ sinfo->lk_pool_size = r.end - r.start;
+
+ DRM_INFO("lk_pool: addr:%pK, size:%pK\n",
+ (void *)sinfo->lk_pool_paddr,
+ (void *)sinfo->lk_pool_size);
+
+child_node_err:
+ of_node_put(node);
+
+parent_node_err:
+ of_node_put(parent);
+
+ return ret;
+}
+
+static int _sde_splash_parse_dt_get_display_node(struct drm_device *dev,
+ struct sde_splash_info *sinfo)
+{
+ unsigned long size = 0;
+ dma_addr_t start;
+ struct device_node *node;
+ int ret = 0, i = 0, len = 0;
+
+ /* get reserved memory for display module */
+ if (of_get_property(dev->dev->of_node, "contiguous-region", &len))
+ sinfo->splash_mem_num = len / sizeof(u32);
+ else
+ sinfo->splash_mem_num = 0;
+
+ sinfo->splash_mem_paddr =
+ kmalloc(sizeof(phys_addr_t) * sinfo->splash_mem_num,
+ GFP_KERNEL);
+ if (!sinfo->splash_mem_paddr) {
+ SDE_ERROR("alloc splash_mem_paddr failed\n");
+ return -ENOMEM;
+ }
+
+ sinfo->splash_mem_size =
+ kmalloc(sizeof(size_t) * sinfo->splash_mem_num,
+ GFP_KERNEL);
+ if (!sinfo->splash_mem_size) {
+ SDE_ERROR("alloc splash_mem_size failed\n");
+ goto error;
+ }
+
+ sinfo->obj = kmalloc(sizeof(struct drm_gem_object *) *
+ sinfo->splash_mem_num, GFP_KERNEL);
+ if (!sinfo->obj) {
+ SDE_ERROR("construct splash gem objects failed\n");
+ goto error;
+ }
+
+ for (i = 0; i < sinfo->splash_mem_num; i++) {
+ node = of_parse_phandle(dev->dev->of_node,
+ "contiguous-region", i);
+
+ if (node) {
+ struct resource r;
+
+ ret = of_address_to_resource(node, 0, &r);
+ if (ret)
+ return ret;
+
+ size = r.end - r.start;
+ start = (dma_addr_t)r.start;
+
+ sinfo->splash_mem_paddr[i] = start;
+ sinfo->splash_mem_size[i] = size;
+
+ DRM_INFO("blk: %d, addr:%pK, size:%pK\n",
+ i, (void *)sinfo->splash_mem_paddr[i],
+ (void *)sinfo->splash_mem_size[i]);
+
+ of_node_put(node);
+ }
+ }
+
+ return ret;
+
+error:
+ kfree(sinfo->splash_mem_paddr);
+ sinfo->splash_mem_paddr = NULL;
+
+ kfree(sinfo->splash_mem_size);
+ sinfo->splash_mem_size = NULL;
+
+ return -ENOMEM;
+}
+
+static bool _sde_splash_lk_check(struct sde_hw_intr *intr)
+{
+ return (SDE_LK_RUNNING_VALUE == SDE_REG_READ(&intr->hw,
+ SCRATCH_REGISTER_1)) ? true : false;
+}
+
+/**
+ * _sde_splash_notify_lk_to_exit.
+ *
+ * Function to monitor LK's status and tell it to exit.
+ */
+static void _sde_splash_notify_lk_exit(struct sde_hw_intr *intr)
+{
+ int i = 0;
+
+ /* first is to write exit signal to scratch register*/
+ SDE_REG_WRITE(&intr->hw, SCRATCH_REGISTER_1, SDE_LK_SHUT_DOWN_VALUE);
+
+ while ((SDE_LK_EXIT_VALUE !=
+ SDE_REG_READ(&intr->hw, SCRATCH_REGISTER_1)) &&
+ (++i < SDE_LK_EXIT_MAX_LOOP)) {
+ DRM_INFO("wait for LK's exit");
+ msleep(20);
+ }
+
+ if (i == SDE_LK_EXIT_MAX_LOOP)
+ SDE_ERROR("Loop LK's exit failed\n");
+}
+
+static int _sde_splash_gem_new(struct drm_device *dev,
+ struct sde_splash_info *sinfo)
+{
+ int i, ret;
+
+ for (i = 0; i < sinfo->splash_mem_num; i++) {
+ sinfo->obj[i] = msm_gem_new(dev,
+ sinfo->splash_mem_size[i], MSM_BO_UNCACHED);
+
+ if (IS_ERR(sinfo->obj[i])) {
+ ret = PTR_ERR(sinfo->obj[i]);
+ SDE_ERROR("failed to allocate gem, ret=%d\n", ret);
+ goto error;
+ }
+ }
+
+ return 0;
+
+error:
+ for (i = 0; i < sinfo->splash_mem_num; i++) {
+ if (sinfo->obj[i])
+ msm_gem_free_object(sinfo->obj[i]);
+ sinfo->obj[i] = NULL;
+ }
+
+ return ret;
+}
+
+static int _sde_splash_get_pages(struct drm_gem_object *obj, phys_addr_t phys)
+{
+ struct msm_gem_object *msm_obj = to_msm_bo(obj);
+ struct page **p;
+ dma_addr_t paddr;
+ int npages = obj->size >> PAGE_SHIFT;
+ int i;
+
+ p = drm_malloc_ab(npages, sizeof(struct page *));
+ if (!p)
+ return -ENOMEM;
+
+ paddr = phys;
+
+ for (i = 0; i < npages; i++) {
+ p[i] = phys_to_page(paddr);
+ paddr += PAGE_SIZE;
+ }
+
+ msm_obj->sgt = drm_prime_pages_to_sg(p, npages);
+ if (IS_ERR(msm_obj->sgt)) {
+ SDE_ERROR("failed to allocate sgt\n");
+ return -ENOMEM;
+ }
+
+ msm_obj->pages = p;
+
+ return 0;
+}
+
+static void _sde_splash_destroy_gem_object(struct msm_gem_object *msm_obj)
+{
+ if (msm_obj->pages) {
+ sg_free_table(msm_obj->sgt);
+ kfree(msm_obj->sgt);
+ drm_free_large(msm_obj->pages);
+ msm_obj->pages = NULL;
+ }
+}
+
+static void _sde_splash_destroy_splash_node(struct sde_splash_info *sinfo)
+{
+ kfree(sinfo->splash_mem_paddr);
+ sinfo->splash_mem_paddr = NULL;
+
+ kfree(sinfo->splash_mem_size);
+ sinfo->splash_mem_size = NULL;
+}
+
+static void _sde_splash_get_connector_ref_cnt(struct sde_splash_info *sinfo,
+ u32 *hdmi_cnt, u32 *dsi_cnt)
+{
+ mutex_lock(&sde_splash_lock);
+ *hdmi_cnt = sinfo->hdmi_connector_cnt;
+ *dsi_cnt = sinfo->dsi_connector_cnt;
+ mutex_unlock(&sde_splash_lock);
+}
+
+static int _sde_splash_free_resource(struct msm_mmu *mmu,
+ struct sde_splash_info *sinfo, enum splash_connector_type conn)
+{
+ struct msm_gem_object *msm_obj = to_msm_bo(sinfo->obj[conn]);
+
+ if (!msm_obj)
+ return -EINVAL;
+
+ if (mmu->funcs && mmu->funcs->unmap)
+ mmu->funcs->unmap(mmu, sinfo->splash_mem_paddr[conn],
+ msm_obj->sgt, NULL);
+
+ _sde_splash_free_bootup_memory_to_system(sinfo->splash_mem_paddr[conn],
+ sinfo->splash_mem_size[conn]);
+
+ _sde_splash_destroy_gem_object(msm_obj);
+
+ return 0;
+}
+
+__ref int sde_splash_init(struct sde_power_handle *phandle, struct msm_kms *kms)
+{
+ struct sde_kms *sde_kms;
+ struct sde_splash_info *sinfo;
+ int i = 0;
+
+ if (!phandle || !kms) {
+ SDE_ERROR("invalid phandle/kms\n");
+ return -EINVAL;
+ }
+
+ sde_kms = to_sde_kms(kms);
+ sinfo = &sde_kms->splash_info;
+
+ sinfo->dsi_connector_cnt = 0;
+ sinfo->hdmi_connector_cnt = 0;
+
+ sde_power_data_bus_bandwidth_ctrl(phandle,
+ sde_kms->core_client, true);
+
+ for (i = 0; i < sinfo->splash_mem_num; i++) {
+ if (!memblock_is_reserved(sinfo->splash_mem_paddr[i])) {
+ SDE_ERROR("failed to reserve memory\n");
+
+ /* withdraw the vote when failed. */
+ sde_power_data_bus_bandwidth_ctrl(phandle,
+ sde_kms->core_client, false);
+
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+void sde_splash_destroy(struct sde_splash_info *sinfo,
+ struct sde_power_handle *phandle,
+ struct sde_power_client *pclient)
+{
+ struct msm_gem_object *msm_obj;
+ int i = 0;
+
+ if (!sinfo || !phandle || !pclient) {
+ SDE_ERROR("invalid sde_kms/phandle/pclient\n");
+ return;
+ }
+
+ for (i = 0; i < sinfo->splash_mem_num; i++) {
+ msm_obj = to_msm_bo(sinfo->obj[i]);
+
+ if (msm_obj)
+ _sde_splash_destroy_gem_object(msm_obj);
+ }
+
+ sde_power_data_bus_bandwidth_ctrl(phandle, pclient, false);
+
+ _sde_splash_destroy_splash_node(sinfo);
+}
+
+/*
+ * sde_splash_parse_dt.
+ * In the function, it will parse and reserve two kinds of memory node.
+ * First is to get the reserved memory for display buffers.
+ * Second is to get the memory node LK's code stack is running on.
+ */
+int sde_splash_parse_dt(struct drm_device *dev)
+{
+ struct msm_drm_private *priv = dev->dev_private;
+ struct sde_kms *sde_kms;
+ struct sde_splash_info *sinfo;
+
+ if (!priv || !priv->kms) {
+ SDE_ERROR("Invalid kms\n");
+ return -EINVAL;
+ }
+
+ sde_kms = to_sde_kms(priv->kms);
+ sinfo = &sde_kms->splash_info;
+
+ if (_sde_splash_parse_dt_get_display_node(dev, sinfo)) {
+ SDE_ERROR("get display node failed\n");
+ return -EINVAL;
+ }
+
+ if (_sde_splash_parse_dt_get_lk_pool_node(dev, sinfo)) {
+ SDE_ERROR("get LK pool node failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int sde_splash_get_handoff_status(struct msm_kms *kms)
+{
+ uint32_t intf_sel = 0;
+ uint32_t split_display = 0;
+ uint32_t num_of_display_on = 0;
+ uint32_t i = 0;
+ struct sde_kms *sde_kms = to_sde_kms(kms);
+ struct sde_rm *rm;
+ struct sde_hw_blk_reg_map *c;
+ struct sde_splash_info *sinfo;
+ struct sde_mdss_cfg *catalog;
+
+ sinfo = &sde_kms->splash_info;
+ if (!sinfo) {
+ SDE_ERROR("%s(%d): invalid splash info\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ rm = &sde_kms->rm;
+
+ if (!rm || !rm->hw_mdp) {
+ SDE_ERROR("invalid rm.\n");
+ return -EINVAL;
+ }
+
+ c = &rm->hw_mdp->hw;
+ if (c) {
+ intf_sel = SDE_REG_READ(c, DISP_INTF_SEL);
+ split_display = SDE_REG_READ(c, SPLIT_DISPLAY_EN);
+ }
+
+ catalog = sde_kms->catalog;
+
+ if (intf_sel != 0) {
+ for (i = 0; i < catalog->intf_count; i++)
+ if ((intf_sel >> i*8) & 0x000000FF)
+ num_of_display_on++;
+
+ /*
+ * For split display enabled - DSI0, DSI1 interfaces are
+ * considered as single display. So decrement
+ * 'num_of_display_on' by 1
+ */
+ if (split_display)
+ num_of_display_on--;
+ }
+
+ if (num_of_display_on) {
+ sinfo->handoff = true;
+ sinfo->program_scratch_regs = true;
+ sinfo->lk_is_exited = false;
+ } else {
+ sinfo->handoff = false;
+ sinfo->program_scratch_regs = false;
+ sinfo->lk_is_exited = true;
+ }
+
+ return 0;
+}
+
+int sde_splash_smmu_map(struct drm_device *dev, struct msm_mmu *mmu,
+ struct sde_splash_info *sinfo)
+{
+ struct msm_gem_object *msm_obj;
+ int i = 0, ret = 0;
+
+ if (!mmu || !sinfo)
+ return -EINVAL;
+
+ /* first is to construct drm_gem_objects for splash memory */
+ if (_sde_splash_gem_new(dev, sinfo))
+ return -ENOMEM;
+
+ /* second is to contruct sgt table for calling smmu map */
+ for (i = 0; i < sinfo->splash_mem_num; i++) {
+ if (_sde_splash_get_pages(sinfo->obj[i],
+ sinfo->splash_mem_paddr[i]))
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < sinfo->splash_mem_num; i++) {
+ msm_obj = to_msm_bo(sinfo->obj[i]);
+
+ if (mmu->funcs && mmu->funcs->map) {
+ ret = mmu->funcs->map(mmu, sinfo->splash_mem_paddr[i],
+ msm_obj->sgt, IOMMU_READ | IOMMU_NOEXEC, NULL);
+
+ if (!ret) {
+ SDE_ERROR("Map blk %d @%pK failed.\n",
+ i, (void *)sinfo->splash_mem_paddr[i]);
+ return ret;
+ }
+ }
+ }
+
+ return ret ? 0 : -ENOMEM;
+}
+
+void sde_splash_setup_connector_count(struct sde_splash_info *sinfo,
+ int connector_type)
+{
+ switch (connector_type) {
+ case DRM_MODE_CONNECTOR_HDMIA:
+ sinfo->hdmi_connector_cnt++;
+ break;
+ case DRM_MODE_CONNECTOR_DSI:
+ sinfo->dsi_connector_cnt++;
+ break;
+ default:
+ SDE_ERROR("invalid connector_type %d\n", connector_type);
+ }
+}
+
+bool sde_splash_get_lk_complete_status(struct sde_splash_info *sinfo)
+{
+ bool ret = 0;
+
+ mutex_lock(&sde_splash_lock);
+ ret = !sinfo->handoff && !sinfo->lk_is_exited;
+ mutex_unlock(&sde_splash_lock);
+
+ return ret;
+}
+
+int sde_splash_clean_up_free_resource(struct msm_kms *kms,
+ struct sde_power_handle *phandle,
+ int connector_type, void *display)
+{
+ struct sde_kms *sde_kms;
+ struct sde_splash_info *sinfo;
+ struct msm_mmu *mmu;
+ struct dsi_display *dsi_display = display;
+ int ret = 0;
+ int hdmi_conn_count = 0;
+ int dsi_conn_count = 0;
+ static const char *last_commit_display_type = "unknown";
+
+ if (!phandle || !kms) {
+ SDE_ERROR("invalid phandle/kms.\n");
+ return -EINVAL;
+ }
+
+ sde_kms = to_sde_kms(kms);
+ sinfo = &sde_kms->splash_info;
+ if (!sinfo) {
+ SDE_ERROR("%s(%d): invalid splash info\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ _sde_splash_get_connector_ref_cnt(sinfo, &hdmi_conn_count,
+ &dsi_conn_count);
+
+ mutex_lock(&sde_splash_lock);
+ if (hdmi_conn_count == 0 && dsi_conn_count == 0 &&
+ !sinfo->lk_is_exited) {
+ /* When both hdmi's and dsi's handoff are finished,
+ * 1. Destroy splash node objects.
+ * 2. Release the memory which LK's stack is running on.
+ * 3. Withdraw AHB data bus bandwidth voting.
+ */
+ DRM_INFO("HDMI and DSI resource handoff is completed\n");
+
+ sinfo->lk_is_exited = true;
+
+ _sde_splash_destroy_splash_node(sinfo);
+
+ _sde_splash_free_bootup_memory_to_system(sinfo->lk_pool_paddr,
+ sinfo->lk_pool_size);
+
+ sde_power_data_bus_bandwidth_ctrl(phandle,
+ sde_kms->core_client, false);
+
+ mutex_unlock(&sde_splash_lock);
+ return 0;
+ }
+
+ mmu = sde_kms->aspace[0]->mmu;
+
+ switch (connector_type) {
+ case DRM_MODE_CONNECTOR_HDMIA:
+ if (sinfo->hdmi_connector_cnt == 1) {
+ sinfo->hdmi_connector_cnt--;
+
+ ret = _sde_splash_free_resource(mmu,
+ sinfo, SPLASH_HDMI);
+ }
+ break;
+ case DRM_MODE_CONNECTOR_DSI:
+ /*
+ * Basically, we have commits coming on two DSI connectors.
+ * So when releasing DSI resource, it's ensured that the
+ * coming commits should happen on different DSIs, to promise
+ * the handoff has finished on the two DSIs, then it's safe
+ * to release DSI resource, otherwise, problem happens when
+ * freeing memory, while DSI0 or DSI1 is still visiting
+ * the memory.
+ */
+ if (strcmp(dsi_display->display_type, "unknown") &&
+ strcmp(last_commit_display_type,
+ dsi_display->display_type)) {
+ if (sinfo->dsi_connector_cnt > 1)
+ sinfo->dsi_connector_cnt--;
+ else if (sinfo->dsi_connector_cnt == 1) {
+ ret = _sde_splash_free_resource(mmu,
+ sinfo, SPLASH_DSI);
+
+ sinfo->dsi_connector_cnt--;
+ }
+
+ last_commit_display_type = dsi_display->display_type;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ SDE_ERROR("%s: invalid connector_type %d\n",
+ __func__, connector_type);
+ }
+
+ mutex_unlock(&sde_splash_lock);
+
+ return ret;
+}
+
+/*
+ * In below function, it will
+ * 1. Notify LK to exit and wait for exiting is done.
+ * 2. Set DOMAIN_ATTR_EARLY_MAP to 1 to enable stage 1 translation in iommu.
+ */
+int sde_splash_clean_up_exit_lk(struct msm_kms *kms)
+{
+ struct sde_splash_info *sinfo;
+ struct msm_mmu *mmu;
+ struct sde_kms *sde_kms = to_sde_kms(kms);
+ int ret;
+
+ sinfo = &sde_kms->splash_info;
+
+ if (!sinfo) {
+ SDE_ERROR("%s(%d): invalid splash info\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ /* Monitor LK's status and tell it to exit. */
+ mutex_lock(&sde_splash_lock);
+ if (sinfo->program_scratch_regs) {
+ if (_sde_splash_lk_check(sde_kms->hw_intr))
+ _sde_splash_notify_lk_exit(sde_kms->hw_intr);
+
+ sinfo->handoff = false;
+ sinfo->program_scratch_regs = false;
+ }
+ mutex_unlock(&sde_splash_lock);
+
+ if (!sde_kms->aspace[0] || !sde_kms->aspace[0]->mmu) {
+ /* We do not return fault value here, to ensure
+ * flag "lk_is_exited" is set.
+ */
+ SDE_ERROR("invalid mmu\n");
+ WARN_ON(1);
+ } else {
+ mmu = sde_kms->aspace[0]->mmu;
+ /* After LK has exited, set early domain map attribute
+ * to 1 to enable stage 1 translation in iommu driver.
+ */
+ if (mmu->funcs && mmu->funcs->set_property) {
+ ret = mmu->funcs->set_property(mmu,
+ DOMAIN_ATTR_EARLY_MAP, &sinfo->handoff);
+
+ if (ret)
+ SDE_ERROR("set_property failed\n");
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_splash.h b/drivers/gpu/drm/msm/sde/sde_splash.h
new file mode 100644
index 000000000000..babf88335e49
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_splash.h
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef SDE_SPLASH_H_
+#define SDE_SPLASH_H_
+
+#include "msm_kms.h"
+#include "msm_mmu.h"
+
+enum splash_connector_type {
+ SPLASH_DSI = 0,
+ SPLASH_HDMI,
+};
+
+struct sde_splash_info {
+ /* handoff flag */
+ bool handoff;
+
+ /* flag of display scratch registers */
+ bool program_scratch_regs;
+
+ /* to indicate LK is totally exited */
+ bool lk_is_exited;
+
+ /* memory node used for display buffer */
+ uint32_t splash_mem_num;
+
+ /* physical address of memory node for display buffer */
+ phys_addr_t *splash_mem_paddr;
+
+ /* size of memory node */
+ size_t *splash_mem_size;
+
+ /* constructed gem objects for smmu mapping */
+ struct drm_gem_object **obj;
+
+ /* physical address of lk pool */
+ phys_addr_t lk_pool_paddr;
+
+ /* memory size of lk pool */
+ size_t lk_pool_size;
+
+ /* registered hdmi connector count */
+ uint32_t hdmi_connector_cnt;
+
+ /* registered dst connector count */
+ uint32_t dsi_connector_cnt;
+};
+
+/* APIs for early splash handoff functions */
+
+/**
+ * sde_splash_get_handoff_status.
+ *
+ * This function will read DISP_INTF_SEL regsiter to get
+ * the status of early splash.
+ */
+int sde_splash_get_handoff_status(struct msm_kms *kms);
+
+/**
+ * sde_splash_init
+ *
+ * This function will do bandwidth vote and reserved memory
+ */
+int sde_splash_init(struct sde_power_handle *phandle, struct msm_kms *kms);
+
+/**
+ *sde_splash_setup_connector_count
+ *
+ * To count connector numbers for DSI and HDMI respectively.
+ */
+void sde_splash_setup_connector_count(struct sde_splash_info *sinfo,
+ int connector_type);
+
+/**
+ * sde_splash_clean_up_exit_lk.
+ *
+ * Tell LK to exit, and clean up the resource.
+ */
+int sde_splash_clean_up_exit_lk(struct msm_kms *kms);
+
+/**
+ * sde_splash_clean_up_free_resource.
+ *
+ * According to input connector_type, free
+ * HDMI's and DSI's resource respectively.
+ */
+int sde_splash_clean_up_free_resource(struct msm_kms *kms,
+ struct sde_power_handle *phandle,
+ int connector_type, void *display);
+
+/**
+ * sde_splash_parse_dt.
+ *
+ * Parse reserved memory block from DT for early splash.
+ */
+int sde_splash_parse_dt(struct drm_device *dev);
+
+/**
+ * sde_splash_smmu_map.
+ *
+ * Map the physical memory LK visited into iommu driver.
+ */
+int sde_splash_smmu_map(struct drm_device *dev, struct msm_mmu *mmu,
+ struct sde_splash_info *sinfo);
+
+/**
+ * sde_splash_destroy
+ *
+ * Destroy the resource in failed case.
+ */
+void sde_splash_destroy(struct sde_splash_info *sinfo,
+ struct sde_power_handle *phandle,
+ struct sde_power_client *pclient);
+
+/**
+ * sde_splash_get_lk_complete_status
+ *
+ * Get LK's status to check if it has been stopped.
+ */
+bool sde_splash_get_lk_complete_status(struct sde_splash_info *sinfo);
+
+#endif
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
new file mode 100644
index 000000000000..5a0c5e677ed8
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -0,0 +1,2067 @@
+/* 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, "%x %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;
+ 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(ROW_BYTES, GFP_KERNEL);
+ if (!hwbuf) {
+ kfree(dbg->buf);
+ return -ENOMEM;
+ }
+
+ ioptr = dbg->base + dbg->off;
+ tot = 0;
+ _sde_dbg_enable_power(true);
+
+ for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) {
+ memcpy_fromio(hwbuf, ioptr, ROW_BYTES);
+ hex_dump_to_buffer(hwbuf,
+ 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) ioptr) -
+ ((int) (unsigned long) dbg->base),
+ dump_buf);
+
+ ioptr += ROW_BYTES;
+ tot += len;
+ if (tot >= dbg->buf_len)
+ break;
+ }
+
+ _sde_dbg_enable_power(false);
+
+ 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(&reg_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(&reg_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, &reg_base->sub_range_list);
+
+ pr_debug("base %s, range %s, start 0x%X, end 0x%X\n",
+ base_name, range->range_name,
+ range->offset.start, range->offset.end);
+}
+
+void sde_dbg_set_sde_top_offset(u32 blk_off)
+{
+ sde_dbg_base.dbgbus_sde.top_blk_off = blk_off;
+}
diff --git a/drivers/gpu/drm/msm/sde_dbg.h b/drivers/gpu/drm/msm/sde_dbg.h
index 271c41f05ce5..74fd4c94b490 100644
--- a/drivers/gpu/drm/msm/sde_dbg.h
+++ b/drivers/gpu/drm/msm/sde_dbg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,34 +29,288 @@ enum sde_dbg_evtlog_flag {
SDE_EVTLOG_ALL = BIT(7)
};
+enum sde_dbg_dump_flag {
+ SDE_DBG_DUMP_IN_LOG = BIT(0),
+ SDE_DBG_DUMP_IN_MEM = BIT(1),
+};
+
+#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG
+#define SDE_EVTLOG_DEFAULT_ENABLE 1
+#else
+#define SDE_EVTLOG_DEFAULT_ENABLE 0
+#endif
+
+/*
+ * evtlog will print this number of entries when it is called through
+ * sysfs node or panic. This prevents kernel log from evtlog message
+ * flood.
+ */
+#define SDE_EVTLOG_PRINT_ENTRY 256
+
+/*
+ * evtlog keeps this number of entries in memory for debug purpose. This
+ * number must be greater than print entry to prevent out of bound evtlog
+ * entry array access.
+ */
+#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4)
+#define SDE_EVTLOG_MAX_DATA 15
+#define SDE_EVTLOG_BUF_MAX 512
+#define SDE_EVTLOG_BUF_ALIGN 32
+
+struct sde_dbg_power_ctrl {
+ void *handle;
+ void *client;
+ int (*enable_fn)(void *handle, void *client, bool enable);
+};
+
+struct sde_dbg_evtlog_log {
+ u32 counter;
+ s64 time;
+ const char *name;
+ int line;
+ u32 data[SDE_EVTLOG_MAX_DATA];
+ u32 data_cnt;
+ int pid;
+};
+
+struct sde_dbg_evtlog {
+ struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY];
+ u32 first;
+ u32 last;
+ u32 curr;
+ u32 next;
+ u32 enable;
+ spinlock_t spin_lock;
+};
+
+extern struct sde_dbg_evtlog *sde_dbg_base_evtlog;
+
+/**
+ * SDE_EVT32 - Write a list of 32bit values to the event log, default area
+ * ... - variable arguments
+ */
+#define SDE_EVT32(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
+ __LINE__, SDE_EVTLOG_DEFAULT, ##__VA_ARGS__, \
+ SDE_EVTLOG_DATA_LIMITER)
+
/**
- * SDE_EVT32 - Write an list of 32bit values as an event into the event log
+ * SDE_EVT32_IRQ - Write a list of 32bit values to the event log, IRQ area
* ... - variable arguments
*/
-#define SDE_EVT32(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_DEFAULT, \
- ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER)
-#define SDE_EVT32_IRQ(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_IRQ, \
- ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER)
+#define SDE_EVT32_IRQ(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
+ __LINE__, SDE_EVTLOG_IRQ, ##__VA_ARGS__, \
+ SDE_EVTLOG_DATA_LIMITER)
-#define SDE_DBG_DUMP(...) \
- sde_dbg_dump(false, __func__, ##__VA_ARGS__, \
+/**
+ * SDE_DBG_DUMP - trigger dumping of all sde_dbg facilities
+ * @va_args: list of named register dump ranges and regions to dump, as
+ * registered previously through sde_dbg_reg_register_base and
+ * sde_dbg_reg_register_dump_range.
+ * Including the special name "panic" will trigger a panic after
+ * the dumping work has completed.
+ */
+#define SDE_DBG_DUMP(...) sde_dbg_dump(false, __func__, ##__VA_ARGS__, \
SDE_DBG_DUMP_DATA_LIMITER)
-#define SDE_DBG_DUMP_WQ(...) \
- sde_dbg_dump(true, __func__, ##__VA_ARGS__, \
+/**
+ * SDE_DBG_DUMP_WQ - trigger dumping of all sde_dbg facilities, queuing the work
+ * @va_args: list of named register dump ranges and regions to dump, as
+ * registered previously through sde_dbg_reg_register_base and
+ * sde_dbg_reg_register_dump_range.
+ * Including the special name "panic" will trigger a panic after
+ * the dumping work has completed.
+ */
+#define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \
SDE_DBG_DUMP_DATA_LIMITER)
#if defined(CONFIG_DEBUG_FS)
-int sde_evtlog_init(struct dentry *debugfs_root);
-void sde_evtlog_destroy(void);
-void sde_evtlog(const char *name, int line, int flag, ...);
-void sde_dbg_dump(bool queue, const char *name, ...);
+/**
+ * sde_evtlog_init - allocate a new event log object
+ * Returns: evtlog or -ERROR
+ */
+struct sde_dbg_evtlog *sde_evtlog_init(void);
+
+/**
+ * sde_evtlog_destroy - destroy previously allocated event log
+ * @evtlog: pointer to evtlog
+ * Returns: none
+ */
+void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog);
+
+/**
+ * sde_evtlog_log - log an entry into the event log.
+ * log collection may be enabled/disabled entirely via debugfs
+ * log area collection may be filtered by user provided flags via debugfs.
+ * @evtlog: pointer to evtlog
+ * @name: function name of call site
+ * @line: line number of call site
+ * @flag: log area filter flag checked against user's debugfs request
+ * Returns: none
+ */
+void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
+ int flag, ...);
+
+/**
+ * sde_evtlog_dump_all - print all entries in event log to kernel log
+ * @evtlog: pointer to evtlog
+ * Returns: none
+ */
+void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog);
+
+/**
+ * sde_evtlog_is_enabled - check whether log collection is enabled for given
+ * event log and log area flag
+ * @evtlog: pointer to evtlog
+ * @flag: log area filter flag
+ * Returns: none
+ */
+bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag);
+
+/**
+ * sde_evtlog_dump_to_buffer - print content of event log to the given buffer
+ * @evtlog: pointer to evtlog
+ * @evtlog_buf: target buffer to print into
+ * @evtlog_buf_size: size of target buffer
+ * Returns: number of bytes written to buffer
+ */
+ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+ char *evtlog_buf, ssize_t evtlog_buf_size);
+
+/**
+ * sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset
+ * @hwversion: Chipset revision
+ */
+void sde_dbg_init_dbg_buses(u32 hwversion);
+
+/**
+ * sde_dbg_init - initialize global sde debug facilities: evtlog, regdump
+ * @debugfs_root: debugfs root in which to create sde debug entries
+ * @dev: device handle
+ * @power_ctrl: power control callback structure for enabling clocks
+ * during register dumping
+ * Returns: 0 or -ERROR
+ */
+int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+ struct sde_dbg_power_ctrl *power_ctrl);
+
+/**
+ * sde_dbg_destroy - destroy the global sde debug facilities
+ * Returns: none
+ */
+void sde_dbg_destroy(void);
+
+/**
+ * sde_dbg_dump - trigger dumping of all sde_dbg facilities
+ * @queue_work: whether to queue the dumping work to the work_struct
+ * @name: string indicating origin of dump
+ * @va_args: list of named register dump ranges and regions to dump, as
+ * registered previously through sde_dbg_reg_register_base and
+ * sde_dbg_reg_register_dump_range.
+ * Including the special name "panic" will trigger a panic after
+ * the dumping work has completed.
+ * Returns: none
+ */
+void sde_dbg_dump(bool queue_work, const char *name, ...);
+
+/**
+ * sde_dbg_reg_register_base - register a hw register address section for later
+ * dumping. call this before calling sde_dbg_reg_register_dump_range
+ * to be able to specify sub-ranges within the base hw range.
+ * @name: name of base region
+ * @base: base pointer of region
+ * @max_offset: length of region
+ * Returns: 0 or -ERROR
+ */
+int sde_dbg_reg_register_base(const char *name, void __iomem *base,
+ size_t max_offset);
+
+/**
+ * sde_dbg_reg_register_dump_range - register a hw register sub-region for
+ * later register dumping associated with base specified by
+ * sde_dbg_reg_register_base
+ * @base_name: name of base region
+ * @range_name: name of sub-range within base region
+ * @offset_start: sub-range's start offset from base's base pointer
+ * @offset_end: sub-range's end offset from base's base pointer
+ * @xin_id: xin id
+ * Returns: none
+ */
+void sde_dbg_reg_register_dump_range(const char *base_name,
+ const char *range_name, u32 offset_start, u32 offset_end,
+ uint32_t xin_id);
+
+/**
+ * sde_dbg_set_sde_top_offset - set the target specific offset from mdss base
+ * address of the top registers. Used for accessing debug bus controls.
+ * @blk_off: offset from mdss base of the top block
+ */
+void sde_dbg_set_sde_top_offset(u32 blk_off);
#else
-static inline int sde_evtlog_init(struct dentry *debugfs_root) { return 0; }
-static inline void sde_evtlog(const char *name, int line, flag, ...) {}
-static inline void sde_evtlog_destroy(void) { }
-static inline void sde_dbg_dump(bool queue, const char *name, ...) {}
-#endif
+static inline struct sde_dbg_evtlog *sde_evtlog_init(void)
+{
+ return NULL;
+}
+
+static inline void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
+{
+}
+
+static inline void sde_evtlog_log(struct sde_dbg_evtlog *evtlog,
+ const char *name, int line, int flag, ...)
+{
+}
+
+static inline void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
+{
+}
+
+static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog,
+ u32 flag)
+{
+ return false;
+}
+
+static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+ char *evtlog_buf, ssize_t evtlog_buf_size)
+{
+ return 0;
+}
+
+void sde_dbg_init_dbg_buses(u32 hwversion)
+{
+}
+
+static inline int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+ struct sde_dbg_power_ctrl *power_ctrl)
+{
+ return 0;
+}
+
+static inline void sde_dbg_destroy(void)
+{
+}
+
+static inline void sde_dbg_dump(bool queue_work, const char *name, ...)
+{
+}
+
+static inline int sde_dbg_reg_register_base(const char *name,
+ void __iomem *base, size_t max_offset)
+{
+ return 0;
+}
+
+static inline void sde_dbg_reg_register_dump_range(const char *base_name,
+ const char *range_name, u32 offset_start, u32 offset_end,
+ uint32_t xin_id)
+{
+}
+
+void sde_dbg_set_sde_top_offset(u32 blk_off)
+{
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
#endif /* SDE_DBG_H_ */
diff --git a/drivers/gpu/drm/msm/sde_dbg_evtlog.c b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
index 72832776659d..759bdab48840 100644
--- a/drivers/gpu/drm/msm/sde_dbg_evtlog.c
+++ b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-#define pr_fmt(fmt) "sde_evtlog:[%s] " fmt, __func__
+#define pr_fmt(fmt) "sde_dbg:[%s] " fmt, __func__
#include <linux/delay.h>
#include <linux/spinlock.h>
@@ -18,77 +18,36 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/dma-buf.h>
+#include <linux/slab.h>
#include "sde_dbg.h"
#include "sde_trace.h"
-#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG
-#define SDE_EVTLOG_DEFAULT_ENABLE 1
-#else
-#define SDE_EVTLOG_DEFAULT_ENABLE 0
-#endif
-
-#define SDE_DBG_DEFAULT_PANIC 1
-
-/*
- * evtlog will print this number of entries when it is called through
- * sysfs node or panic. This prevents kernel log from evtlog message
- * flood.
- */
-#define SDE_EVTLOG_PRINT_ENTRY 256
-
-/*
- * evtlog keeps this number of entries in memory for debug purpose. This
- * number must be greater than print entry to prevent out of bound evtlog
- * entry array access.
- */
-#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4)
-#define SDE_EVTLOG_MAX_DATA 15
-#define SDE_EVTLOG_BUF_MAX 512
-#define SDE_EVTLOG_BUF_ALIGN 32
-
-DEFINE_SPINLOCK(sde_evtloglock);
-
-struct tlog {
- u32 counter;
- s64 time;
- const char *name;
- int line;
- u32 data[SDE_EVTLOG_MAX_DATA];
- u32 data_cnt;
- int pid;
-};
-
-static struct sde_dbg_evtlog {
- struct tlog logs[SDE_EVTLOG_ENTRY];
- u32 first;
- u32 last;
- u32 curr;
- struct dentry *evtlog;
- u32 evtlog_enable;
- u32 panic_on_err;
- struct work_struct evtlog_dump_work;
- bool work_panic;
-} sde_dbg_evtlog;
-
-static inline bool sde_evtlog_is_enabled(u32 flag)
+bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag)
{
- return (flag & sde_dbg_evtlog.evtlog_enable) ||
- (flag == SDE_EVTLOG_ALL && sde_dbg_evtlog.evtlog_enable);
+ if (!evtlog)
+ return false;
+
+ return (flag & evtlog->enable) ||
+ (flag == SDE_EVTLOG_ALL && evtlog->enable);
}
-void sde_evtlog(const char *name, int line, int flag, ...)
+void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
+ int flag, ...)
{
unsigned long flags;
int i, val = 0;
va_list args;
- struct tlog *log;
+ struct sde_dbg_evtlog_log *log;
+
+ if (!evtlog)
+ return;
- if (!sde_evtlog_is_enabled(flag))
+ if (!sde_evtlog_is_enabled(evtlog, flag))
return;
- spin_lock_irqsave(&sde_evtloglock, flags);
- log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.curr];
+ spin_lock_irqsave(&evtlog->spin_lock, flags);
+ log = &evtlog->logs[evtlog->curr];
log->time = ktime_to_us(ktime_get());
log->name = name;
log->line = line;
@@ -106,26 +65,27 @@ void sde_evtlog(const char *name, int line, int flag, ...)
}
va_end(args);
log->data_cnt = i;
- sde_dbg_evtlog.curr = (sde_dbg_evtlog.curr + 1) % SDE_EVTLOG_ENTRY;
- sde_dbg_evtlog.last++;
+ evtlog->curr = (evtlog->curr + 1) % SDE_EVTLOG_ENTRY;
+ evtlog->last++;
trace_sde_evtlog(name, line, i > 0 ? log->data[0] : 0,
i > 1 ? log->data[1] : 0);
- spin_unlock_irqrestore(&sde_evtloglock, flags);
+ spin_unlock_irqrestore(&evtlog->spin_lock, flags);
}
/* always dump the last entries which are not dumped yet */
-static bool _sde_evtlog_dump_calc_range(void)
+static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog)
{
- static u32 next;
bool need_dump = true;
unsigned long flags;
- struct sde_dbg_evtlog *evtlog = &sde_dbg_evtlog;
- spin_lock_irqsave(&sde_evtloglock, flags);
+ if (!evtlog)
+ return false;
+
+ spin_lock_irqsave(&evtlog->spin_lock, flags);
- evtlog->first = next;
+ evtlog->first = evtlog->next;
if (evtlog->last == evtlog->first) {
need_dump = false;
@@ -143,27 +103,34 @@ static bool _sde_evtlog_dump_calc_range(void)
evtlog->last - evtlog->first);
evtlog->first = evtlog->last - SDE_EVTLOG_PRINT_ENTRY;
}
- next = evtlog->first + 1;
+ evtlog->next = evtlog->first + 1;
dump_exit:
- spin_unlock_irqrestore(&sde_evtloglock, flags);
+ spin_unlock_irqrestore(&evtlog->spin_lock, flags);
return need_dump;
}
-static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
+ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+ char *evtlog_buf, ssize_t evtlog_buf_size)
{
int i;
ssize_t off = 0;
- struct tlog *log, *prev_log;
+ struct sde_dbg_evtlog_log *log, *prev_log;
unsigned long flags;
- spin_lock_irqsave(&sde_evtloglock, flags);
+ if (!evtlog || !evtlog_buf)
+ return 0;
- log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.first %
- SDE_EVTLOG_ENTRY];
+ /* update markers, exit if nothing to print */
+ if (!_sde_evtlog_dump_calc_range(evtlog))
+ return 0;
+
+ spin_lock_irqsave(&evtlog->spin_lock, flags);
- prev_log = &sde_dbg_evtlog.logs[(sde_dbg_evtlog.first - 1) %
+ log = &evtlog->logs[evtlog->first % SDE_EVTLOG_ENTRY];
+
+ prev_log = &evtlog->logs[(evtlog->first - 1) %
SDE_EVTLOG_ENTRY];
off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d",
@@ -175,7 +142,7 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
}
off += snprintf((evtlog_buf + off), (evtlog_buf_size - off),
- "=>[%-8d:%-11llu:%9llu][%-4d]:", sde_dbg_evtlog.first,
+ "=>[%-8d:%-11llu:%9llu][%-4d]:", evtlog->first,
log->time, (log->time - prev_log->time), log->pid);
for (i = 0; i < log->data_cnt; i++)
@@ -184,143 +151,37 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n");
- spin_unlock_irqrestore(&sde_evtloglock, flags);
+ spin_unlock_irqrestore(&evtlog->spin_lock, flags);
return off;
}
-static void _sde_evtlog_dump_all(void)
-{
- char evtlog_buf[SDE_EVTLOG_BUF_MAX];
-
- while (_sde_evtlog_dump_calc_range()) {
- sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX);
- pr_info("%s", evtlog_buf);
- }
-}
-
-static void _sde_dump_array(bool dead, const char *name)
-{
- _sde_evtlog_dump_all();
-
- if (dead && sde_dbg_evtlog.panic_on_err)
- panic(name);
-}
-
-static void _sde_dump_work(struct work_struct *work)
+void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
{
- _sde_dump_array(sde_dbg_evtlog.work_panic, "evtlog_workitem");
-}
-
-void sde_dbg_dump(bool queue, const char *name, ...)
-{
- int i;
- bool dead = false;
- va_list args;
- char *blk_name = NULL;
-
- if (!sde_evtlog_is_enabled(SDE_EVTLOG_DEFAULT))
- return;
+ char buf[SDE_EVTLOG_BUF_MAX];
- if (queue && work_pending(&sde_dbg_evtlog.evtlog_dump_work))
+ if (!evtlog)
return;
- va_start(args, name);
- for (i = 0; i < SDE_EVTLOG_MAX_DATA; i++) {
- blk_name = va_arg(args, char*);
- if (IS_ERR_OR_NULL(blk_name))
- break;
-
- if (!strcmp(blk_name, "panic"))
- dead = true;
- }
- va_end(args);
-
- if (queue) {
- /* schedule work to dump later */
- sde_dbg_evtlog.work_panic = dead;
- schedule_work(&sde_dbg_evtlog.evtlog_dump_work);
- } else {
- _sde_dump_array(dead, name);
- }
+ while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf)))
+ pr_info("%s", buf);
}
-static int sde_evtlog_dump_open(struct inode *inode, struct file *file)
+struct sde_dbg_evtlog *sde_evtlog_init(void)
{
- /* non-seekable */
- file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
- file->private_data = inode->i_private;
- return 0;
-}
-
-static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff,
- size_t count, loff_t *ppos)
-{
- ssize_t len = 0;
- char evtlog_buf[SDE_EVTLOG_BUF_MAX];
-
- if (_sde_evtlog_dump_calc_range()) {
- len = sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX);
- if (copy_to_user(buff, evtlog_buf, len))
- return -EFAULT;
- *ppos += len;
- }
-
- return len;
-}
-
-static ssize_t sde_evtlog_dump_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- _sde_evtlog_dump_all();
-
- if (sde_dbg_evtlog.panic_on_err)
- panic("sde");
-
- return count;
-}
-
-static const struct file_operations sde_evtlog_fops = {
- .open = sde_evtlog_dump_open,
- .read = sde_evtlog_dump_read,
- .write = sde_evtlog_dump_write,
-};
-
-int sde_evtlog_init(struct dentry *debugfs_root)
-{
- int i;
-
- sde_dbg_evtlog.evtlog = debugfs_create_dir("evt_dbg", debugfs_root);
- if (IS_ERR_OR_NULL(sde_dbg_evtlog.evtlog)) {
- pr_err("debugfs_create_dir fail, error %ld\n",
- PTR_ERR(sde_dbg_evtlog.evtlog));
- sde_dbg_evtlog.evtlog = NULL;
- return -ENODEV;
- }
-
- INIT_WORK(&sde_dbg_evtlog.evtlog_dump_work, _sde_dump_work);
- sde_dbg_evtlog.work_panic = false;
-
- for (i = 0; i < SDE_EVTLOG_ENTRY; i++)
- sde_dbg_evtlog.logs[i].counter = i;
-
- debugfs_create_file("dump", 0644, sde_dbg_evtlog.evtlog, NULL,
- &sde_evtlog_fops);
- debugfs_create_u32("enable", 0644, sde_dbg_evtlog.evtlog,
- &sde_dbg_evtlog.evtlog_enable);
- debugfs_create_u32("panic", 0644, sde_dbg_evtlog.evtlog,
- &sde_dbg_evtlog.panic_on_err);
+ struct sde_dbg_evtlog *evtlog;
- sde_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE;
- sde_dbg_evtlog.panic_on_err = SDE_DBG_DEFAULT_PANIC;
+ evtlog = kzalloc(sizeof(*evtlog), GFP_KERNEL);
+ if (!evtlog)
+ return ERR_PTR(-ENOMEM);
- pr_info("evtlog_status: enable:%d, panic:%d\n",
- sde_dbg_evtlog.evtlog_enable, sde_dbg_evtlog.panic_on_err);
+ spin_lock_init(&evtlog->spin_lock);
+ evtlog->enable = SDE_EVTLOG_DEFAULT_ENABLE;
- return 0;
+ return evtlog;
}
-void sde_evtlog_destroy(void)
+void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
{
- debugfs_remove(sde_dbg_evtlog.evtlog);
+ kfree(evtlog);
}
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c
index 68246253bb70..50667c5921a0 100644
--- a/drivers/gpu/drm/msm/sde_edid_parser.c
+++ b/drivers/gpu/drm/msm/sde_edid_parser.c
@@ -549,6 +549,12 @@ int _sde_edid_update_modes(struct drm_connector *connector,
{
int rc = 0;
struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
+ struct drm_display_info *disp_info;
+
+ disp_info = &connector->display_info;
+
+ if (disp_info)
+ disp_info->edid_hdmi_dc_modes = 0;
SDE_EDID_DEBUG("%s +", __func__);
if (edid_ctrl->edid) {
diff --git a/drivers/gpu/drm/msm/sde_hdcp_1x.c b/drivers/gpu/drm/msm/sde_hdcp_1x.c
index 3aba9e307732..d08cf13c448d 100644
--- a/drivers/gpu/drm/msm/sde_hdcp_1x.c
+++ b/drivers/gpu/drm/msm/sde_hdcp_1x.c
@@ -751,11 +751,24 @@ error:
static void sde_hdcp_1x_enable_sink_irq_hpd(struct sde_hdcp_1x *hdcp)
{
int rc;
+ u8 const required_major = 1, required_minor = 2;
+ u8 sink_major = 0, sink_minor = 0;
u8 enable_hpd_irq = 0x1;
+ u16 version;
- if (hdcp->current_tp.ds_type != DS_REPEATER)
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI)
return;
+ version = *hdcp->init_data.version;
+ sink_major = (version >> 4) & 0x0f;
+ sink_minor = version & 0x0f;
+
+ if ((sink_minor < required_minor) || (sink_major < required_major) ||
+ (hdcp->current_tp.ds_type != DS_REPEATER)) {
+ pr_debug("sink irq hpd not enabled\n");
+ return;
+ }
+
rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.ainfo, &enable_hpd_irq);
if (IS_ERR_VALUE(rc))
SDE_HDCP_DEBUG("error writing ainfo to sink\n");
@@ -1295,6 +1308,11 @@ static void sde_hdcp_1x_auth_work(struct work_struct *work)
if (rc)
goto end;
+
+end:
+ if (rc && !sde_hdcp_1x_state(HDCP_STATE_INACTIVE))
+ hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
+
/*
* Disabling software DDC before going into part3 to make sure
* there is no Arbitration between software and hardware for DDC
@@ -1302,9 +1320,6 @@ static void sde_hdcp_1x_auth_work(struct work_struct *work)
if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI)
DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io,
HDMI_DDC_ARBITRATION) | (BIT(4)));
-end:
- if (rc && !sde_hdcp_1x_state(HDCP_STATE_INACTIVE))
- hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
sde_hdcp_1x_update_auth_status(hdcp);
}
@@ -1498,6 +1513,10 @@ static int sde_hdcp_1x_isr(void *input)
SDE_HDCP_DEBUG("%s: AUTH FAIL, LINK0_STATUS=0x%08x\n",
SDE_HDCP_STATE_NAME, link_status);
+ /* Clear AUTH_FAIL_INFO as well */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->auth_fail_info_ack));
+
if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) {
hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
sde_hdcp_1x_update_auth_status(hdcp);
@@ -1505,9 +1524,6 @@ static int sde_hdcp_1x_isr(void *input)
complete_all(&hdcp->r0_checked);
}
- /* Clear AUTH_FAIL_INFO as well */
- DSS_REG_W(io, isr->int_reg,
- (hdcp_int_val | isr->auth_fail_info_ack));
}
if (hdcp_int_val & isr->tx_req_int) {
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c
index c794b2c2d21e..6d8f21290aa2 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c
@@ -129,7 +129,7 @@ gf100_bar_init(struct nvkm_bar *base)
if (bar->bar[0].mem) {
addr = nvkm_memory_addr(bar->bar[0].mem) >> 12;
- nvkm_wr32(device, 0x001714, 0xc0000000 | addr);
+ nvkm_wr32(device, 0x001714, 0x80000000 | addr);
}
return 0;
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index d4ac8c837314..8e86cf7da614 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -30,6 +30,7 @@
#include "radeon_audio.h"
#include "atom.h"
#include <linux/backlight.h>
+#include <linux/dmi.h>
extern int atom_debug;
@@ -2183,9 +2184,17 @@ int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder, int fe_idx)
goto assigned;
}
- /* on DCE32 and encoder can driver any block so just crtc id */
+ /*
+ * On DCE32 any encoder can drive any block so usually just use crtc id,
+ * but Apple thinks different at least on iMac10,1, so there use linkb,
+ * otherwise the internal eDP panel will stay dark.
+ */
if (ASIC_IS_DCE32(rdev)) {
- enc_idx = radeon_crtc->crtc_id;
+ if (dmi_match(DMI_PRODUCT_NAME, "iMac10,1"))
+ enc_idx = (dig->linkb) ? 1 : 0;
+ else
+ enc_idx = radeon_crtc->crtc_id;
+
goto assigned;
}
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c
index 3c32f095a873..2ccf81168d1e 100644
--- a/drivers/gpu/drm/radeon/ci_dpm.c
+++ b/drivers/gpu/drm/radeon/ci_dpm.c
@@ -782,6 +782,12 @@ bool ci_dpm_vblank_too_short(struct radeon_device *rdev)
if (r600_dpm_get_vrefresh(rdev) > 120)
return true;
+ /* disable mclk switching if the refresh is >120Hz, even if the
+ * blanking period would allow it
+ */
+ if (r600_dpm_get_vrefresh(rdev) > 120)
+ return true;
+
if (vblank_time < switch_limit)
return true;
else
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 48cb19949ca3..9befd624a5f0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -148,8 +148,8 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
/* Signal polarities */
- value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
- | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
+ value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
+ | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
| DSMR_DIPM_DE | DSMR_CSPM;
rcar_du_crtc_write(rcrtc, DSMR, value);
@@ -171,7 +171,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
mode->crtc_vsync_start - 1);
rcar_du_crtc_write(rcrtc, VCR, mode->crtc_vtotal - 1);
- rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start);
+ rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start - 1);
rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
}
@@ -282,26 +282,6 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc)
* Page Flip
*/
-void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
- struct drm_file *file)
-{
- struct drm_pending_vblank_event *event;
- struct drm_device *dev = rcrtc->crtc.dev;
- unsigned long flags;
-
- /* Destroy the pending vertical blanking event associated with the
- * pending page flip, if any, and disable vertical blanking interrupts.
- */
- spin_lock_irqsave(&dev->event_lock, flags);
- event = rcrtc->event;
- if (event && event->base.file_priv == file) {
- rcrtc->event = NULL;
- event->base.destroy(&event->base);
- drm_crtc_vblank_put(&rcrtc->crtc);
- }
- spin_unlock_irqrestore(&dev->event_lock, flags);
-}
-
static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
{
struct drm_pending_vblank_event *event;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index 4b95d9d08c49..2bbe3f5aab65 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -67,8 +67,6 @@ enum rcar_du_output {
int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index);
void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable);
-void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
- struct drm_file *file);
void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 40422f6b645e..bb9cd35d7fdf 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -144,91 +144,6 @@ MODULE_DEVICE_TABLE(of, rcar_du_of_table);
* DRM operations
*/
-static int rcar_du_unload(struct drm_device *dev)
-{
- struct rcar_du_device *rcdu = dev->dev_private;
-
- if (rcdu->fbdev)
- drm_fbdev_cma_fini(rcdu->fbdev);
-
- drm_kms_helper_poll_fini(dev);
- drm_mode_config_cleanup(dev);
- drm_vblank_cleanup(dev);
-
- dev->irq_enabled = 0;
- dev->dev_private = NULL;
-
- return 0;
-}
-
-static int rcar_du_load(struct drm_device *dev, unsigned long flags)
-{
- struct platform_device *pdev = dev->platformdev;
- struct device_node *np = pdev->dev.of_node;
- struct rcar_du_device *rcdu;
- struct resource *mem;
- int ret;
-
- if (np == NULL) {
- dev_err(dev->dev, "no platform data\n");
- return -ENODEV;
- }
-
- rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
- if (rcdu == NULL) {
- dev_err(dev->dev, "failed to allocate private data\n");
- return -ENOMEM;
- }
-
- init_waitqueue_head(&rcdu->commit.wait);
-
- rcdu->dev = &pdev->dev;
- rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
- rcdu->ddev = dev;
- dev->dev_private = rcdu;
-
- /* I/O resources */
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(rcdu->mmio))
- return PTR_ERR(rcdu->mmio);
-
- /* Initialize vertical blanking interrupts handling. Start with vblank
- * disabled for all CRTCs.
- */
- ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to initialize vblank\n");
- goto done;
- }
-
- /* DRM/KMS objects */
- ret = rcar_du_modeset_init(rcdu);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret);
- goto done;
- }
-
- dev->irq_enabled = 1;
-
- platform_set_drvdata(pdev, rcdu);
-
-done:
- if (ret)
- rcar_du_unload(dev);
-
- return ret;
-}
-
-static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file)
-{
- struct rcar_du_device *rcdu = dev->dev_private;
- unsigned int i;
-
- for (i = 0; i < rcdu->num_crtcs; ++i)
- rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
-}
-
static void rcar_du_lastclose(struct drm_device *dev)
{
struct rcar_du_device *rcdu = dev->dev_private;
@@ -269,11 +184,7 @@ static const struct file_operations rcar_du_fops = {
static struct drm_driver rcar_du_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME
| DRIVER_ATOMIC,
- .load = rcar_du_load,
- .unload = rcar_du_unload,
- .preclose = rcar_du_preclose,
.lastclose = rcar_du_lastclose,
- .set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = rcar_du_enable_vblank,
.disable_vblank = rcar_du_disable_vblank,
@@ -333,18 +244,104 @@ static const struct dev_pm_ops rcar_du_pm_ops = {
* Platform driver
*/
-static int rcar_du_probe(struct platform_device *pdev)
+static int rcar_du_remove(struct platform_device *pdev)
{
- return drm_platform_init(&rcar_du_driver, pdev);
+ struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
+ struct drm_device *ddev = rcdu->ddev;
+
+ mutex_lock(&ddev->mode_config.mutex);
+ drm_connector_unplug_all(ddev);
+ mutex_unlock(&ddev->mode_config.mutex);
+
+ drm_dev_unregister(ddev);
+
+ if (rcdu->fbdev)
+ drm_fbdev_cma_fini(rcdu->fbdev);
+
+ drm_kms_helper_poll_fini(ddev);
+ drm_mode_config_cleanup(ddev);
+
+ drm_dev_unref(ddev);
+
+ return 0;
}
-static int rcar_du_remove(struct platform_device *pdev)
+static int rcar_du_probe(struct platform_device *pdev)
{
- struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ struct rcar_du_device *rcdu;
+ struct drm_connector *connector;
+ struct drm_device *ddev;
+ struct resource *mem;
+ int ret;
+
+ if (np == NULL) {
+ dev_err(&pdev->dev, "no device tree node\n");
+ return -ENODEV;
+ }
- drm_put_dev(rcdu->ddev);
+ /* Allocate and initialize the DRM and R-Car device structures. */
+ rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
+ if (rcdu == NULL)
+ return -ENOMEM;
+
+ init_waitqueue_head(&rcdu->commit.wait);
+
+ rcdu->dev = &pdev->dev;
+ rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
+
+ platform_set_drvdata(pdev, rcdu);
+
+ /* I/O resources */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(rcdu->mmio))
+ return PTR_ERR(rcdu->mmio);
+
+ /* DRM/KMS objects */
+ ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev);
+ if (!ddev)
+ return -ENOMEM;
+
+ drm_dev_set_unique(ddev, dev_name(&pdev->dev));
+
+ rcdu->ddev = ddev;
+ ddev->dev_private = rcdu;
+
+ ret = rcar_du_modeset_init(rcdu);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret);
+ goto error;
+ }
+
+ ddev->irq_enabled = 1;
+
+ /* Register the DRM device with the core and the connectors with
+ * sysfs.
+ */
+ ret = drm_dev_register(ddev, 0);
+ if (ret)
+ goto error;
+
+ mutex_lock(&ddev->mode_config.mutex);
+ drm_for_each_connector(connector, ddev) {
+ ret = drm_connector_register(connector);
+ if (ret < 0)
+ break;
+ }
+ mutex_unlock(&ddev->mode_config.mutex);
+
+ if (ret < 0)
+ goto error;
+
+ DRM_INFO("Device %s probed\n", dev_name(&pdev->dev));
return 0;
+
+error:
+ rcar_du_remove(pdev);
+
+ return ret;
}
static struct platform_driver rcar_du_platform_driver = {
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
index 96f2eb43713c..6038be93c58d 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
@@ -55,12 +55,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = {
.best_encoder = rcar_du_connector_best_encoder,
};
-static void rcar_du_hdmi_connector_destroy(struct drm_connector *connector)
-{
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-
static enum drm_connector_status
rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force)
{
@@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.detect = rcar_du_hdmi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = rcar_du_hdmi_connector_destroy,
+ .destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
@@ -108,9 +102,6 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
return ret;
drm_connector_helper_add(connector, &connector_helper_funcs);
- ret = drm_connector_register(connector);
- if (ret < 0)
- return ret;
connector->dpms = DRM_MODE_DPMS_OFF;
drm_object_property_set_value(&connector->base,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index ca12e8ca5552..2b75a4891dec 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -642,13 +642,13 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
}
ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector);
- of_node_put(encoder);
- of_node_put(connector);
-
if (ret && ret != -EPROBE_DEFER)
dev_warn(rcdu->dev,
- "failed to initialize encoder %s (%d), skipping\n",
- encoder->full_name, ret);
+ "failed to initialize encoder %s on output %u (%d), skipping\n",
+ of_node_full_name(encoder), output, ret);
+
+ of_node_put(encoder);
+ of_node_put(connector);
return ret;
}
@@ -761,6 +761,13 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
if (ret < 0)
return ret;
+ /* Initialize vertical blanking interrupts handling. Start with vblank
+ * disabled for all CRTCs.
+ */
+ ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1);
+ if (ret < 0)
+ return ret;
+
/* Initialize the groups. */
num_groups = DIV_ROUND_UP(rcdu->num_crtcs, 2);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
index 0c43032fc693..e905f5da7aaa 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
@@ -62,12 +62,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = {
.best_encoder = rcar_du_connector_best_encoder,
};
-static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
-{
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-
static enum drm_connector_status
rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
{
@@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.detect = rcar_du_lvds_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = rcar_du_lvds_connector_destroy,
+ .destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
@@ -117,9 +111,6 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
return ret;
drm_connector_helper_add(connector, &connector_helper_funcs);
- ret = drm_connector_register(connector);
- if (ret < 0)
- return ret;
connector->dpms = DRM_MODE_DPMS_OFF;
drm_object_property_set_value(&connector->base,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
index 85043c5bad03..873e04aa9352 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
@@ -56,11 +56,11 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
return ret;
/* PLL clock configuration */
- if (freq <= 38000)
+ if (freq < 39000)
pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M;
- else if (freq <= 60000)
+ else if (freq < 61000)
pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M;
- else if (freq <= 121000)
+ else if (freq < 121000)
pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M;
else
pllcr = LVDPLLCR_PLLDLYCNT_150M;
@@ -102,7 +102,7 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
/* Turn the PLL on, wait for the startup delay, and turn the output
* on.
*/
- lvdcr0 |= LVDCR0_PLLEN;
+ lvdcr0 |= LVDCR0_PLLON;
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
usleep_range(100, 150);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index e0a5d8f93963..9d7e5c99caf6 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -31,12 +31,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = {
.best_encoder = rcar_du_connector_best_encoder,
};
-static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
-{
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-
static enum drm_connector_status
rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
{
@@ -48,7 +42,7 @@ static const struct drm_connector_funcs connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.detect = rcar_du_vga_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = rcar_du_vga_connector_destroy,
+ .destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
@@ -76,9 +70,6 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
return ret;
drm_connector_helper_add(connector, &connector_helper_funcs);
- ret = drm_connector_register(connector);
- if (ret < 0)
- return ret;
connector->dpms = DRM_MODE_DPMS_OFF;
drm_object_property_set_value(&connector->base,
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
index 77cf9289ab65..b1eafd097a79 100644
--- a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
@@ -18,7 +18,7 @@
#define LVDCR0_DMD (1 << 12)
#define LVDCR0_LVMD_MASK (0xf << 8)
#define LVDCR0_LVMD_SHIFT 8
-#define LVDCR0_PLLEN (1 << 4)
+#define LVDCR0_PLLON (1 << 4)
#define LVDCR0_BEN (1 << 2)
#define LVDCR0_LVEN (1 << 1)
#define LVDCR0_LVRES (1 << 0)
diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c
index 6a81e084593b..2b59d80a09b8 100644
--- a/drivers/gpu/drm/virtio/virtgpu_fb.c
+++ b/drivers/gpu/drm/virtio/virtgpu_fb.c
@@ -338,7 +338,7 @@ static int virtio_gpufb_create(struct drm_fb_helper *helper,
info->fbops = &virtio_gpufb_ops;
info->pixmap.flags = FB_PIXMAP_SYSTEM;
- info->screen_base = obj->vmap;
+ info->screen_buffer = obj->vmap;
info->screen_size = obj->gem_base.size;
drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(info, &vfbdev->helper,
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index f300eba95bb1..1244cdf52859 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -81,8 +81,10 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
return -ENOMEM;
size = roundup(size, PAGE_SIZE);
ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size);
- if (ret != 0)
+ if (ret != 0) {
+ kfree(bo);
return ret;
+ }
bo->dumb = false;
virtio_gpu_init_ttm_placement(bo, pinned);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index ecf15cf0c3fd..04fd0f2b6af0 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -471,7 +471,7 @@ static int vmw_cmd_invalid(struct vmw_private *dev_priv,
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
- return capable(CAP_SYS_ADMIN) ? : -EINVAL;
+ return -EINVAL;
}
static int vmw_cmd_ok(struct vmw_private *dev_priv,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 6521ec01413e..c620c7ac1afa 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -807,13 +807,13 @@ static int adreno_of_get_pwrlevels(struct adreno_device *adreno_dev,
struct device_node *parent)
{
struct device_node *node, *child;
+ unsigned int bin = 0;
node = of_find_node_by_name(parent, "qcom,gpu-pwrlevel-bins");
if (node == NULL)
return adreno_of_get_legacy_pwrlevels(adreno_dev, parent);
for_each_child_of_node(node, child) {
- unsigned int bin;
if (of_property_read_u32(child, "qcom,speed-bin", &bin))
continue;
@@ -829,6 +829,8 @@ static int adreno_of_get_pwrlevels(struct adreno_device *adreno_dev,
}
}
+ KGSL_CORE_ERR("GPU speed_bin:%d mismatch for efused bin:%d\n",
+ adreno_dev->speed_bin, bin);
return -ENODEV;
}
@@ -902,6 +904,9 @@ static int adreno_of_get_power(struct adreno_device *adreno_dev,
device->pwrctrl.bus_control = of_property_read_bool(node,
"qcom,bus-control");
+ device->pwrctrl.input_disable = of_property_read_bool(node,
+ "qcom,disable-wake-on-touch");
+
return 0;
}
@@ -1016,15 +1021,19 @@ static int adreno_probe(struct platform_device *pdev)
/* Initialize coresight for the target */
adreno_coresight_init(adreno_dev);
- adreno_input_handler.private = device;
-
#ifdef CONFIG_INPUT
- /*
- * It isn't fatal if we cannot register the input handler. Sad,
- * perhaps, but not fatal
- */
- if (input_register_handler(&adreno_input_handler))
- KGSL_DRV_ERR(device, "Unable to register the input handler\n");
+ if (!device->pwrctrl.input_disable) {
+ adreno_input_handler.private = device;
+ /*
+ * It isn't fatal if we cannot register the input handler. Sad,
+ * perhaps, but not fatal
+ */
+ if (input_register_handler(&adreno_input_handler)) {
+ adreno_input_handler.private = NULL;
+ KGSL_DRV_ERR(device,
+ "Unable to register the input handler\n");
+ }
+ }
#endif
out:
if (status) {
@@ -1076,7 +1085,8 @@ static int adreno_remove(struct platform_device *pdev)
_adreno_free_memories(adreno_dev);
#ifdef CONFIG_INPUT
- input_unregister_handler(&adreno_input_handler);
+ if (adreno_input_handler.private)
+ input_unregister_handler(&adreno_input_handler);
#endif
adreno_sysfs_close(adreno_dev);
@@ -1153,6 +1163,10 @@ static int adreno_init(struct kgsl_device *device)
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
int ret;
+ if (!adreno_is_a3xx(adreno_dev))
+ kgsl_sharedmem_set(device, &device->scratch, 0, 0,
+ device->scratch.size);
+
ret = kgsl_pwrctrl_change_state(device, KGSL_STATE_INIT);
if (ret)
return ret;
@@ -1309,6 +1323,10 @@ static int _adreno_start(struct adreno_device *adreno_dev)
/* make sure ADRENO_DEVICE_STARTED is not set here */
BUG_ON(test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv));
+ /* disallow l2pc during wake up to improve GPU wake up time */
+ kgsl_pwrctrl_update_l2pc(&adreno_dev->dev,
+ KGSL_L2PC_WAKEUP_TIMEOUT);
+
pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
pmqos_wakeup_vote);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 218d08e6dfc3..4a0acdcf8844 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -568,6 +568,8 @@ enum adreno_regs {
ADRENO_REG_RBBM_RBBM_CTL,
ADRENO_REG_UCHE_INVALIDATE0,
ADRENO_REG_UCHE_INVALIDATE1,
+ ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO,
+ ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI,
ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO,
ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI,
ADRENO_REG_RBBM_SECVID_TRUST_CONTROL,
@@ -1508,21 +1510,60 @@ static inline void adreno_ringbuffer_set_pagetable(struct adreno_ringbuffer *rb,
spin_unlock_irqrestore(&rb->preempt_lock, flags);
}
+static inline bool is_power_counter_overflow(struct adreno_device *adreno_dev,
+ unsigned int reg, unsigned int prev_val, unsigned int *perfctr_pwr_hi)
+{
+ unsigned int val;
+ bool ret = false;
+
+ /*
+ * If prev_val is zero, it is first read after perf counter reset.
+ * So set perfctr_pwr_hi register to zero.
+ */
+ if (prev_val == 0) {
+ *perfctr_pwr_hi = 0;
+ return ret;
+ }
+ adreno_readreg(adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, &val);
+ if (val != *perfctr_pwr_hi) {
+ *perfctr_pwr_hi = val;
+ ret = true;
+ }
+ return ret;
+}
+
static inline unsigned int counter_delta(struct kgsl_device *device,
unsigned int reg, unsigned int *counter)
{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
unsigned int val;
unsigned int ret = 0;
+ bool overflow = true;
+ static unsigned int perfctr_pwr_hi;
/* Read the value */
kgsl_regread(device, reg, &val);
+ if (adreno_is_a5xx(adreno_dev) && reg == adreno_getreg
+ (adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO))
+ overflow = is_power_counter_overflow(adreno_dev, reg,
+ *counter, &perfctr_pwr_hi);
+
/* Return 0 for the first read */
if (*counter != 0) {
- if (val < *counter)
- ret = (0xFFFFFFFF - *counter) + val;
- else
+ if (val >= *counter) {
ret = val - *counter;
+ } else if (overflow == true) {
+ ret = (0xFFFFFFFF - *counter) + val;
+ } else {
+ /*
+ * Since KGSL got abnormal value from the counter,
+ * We will drop the value from being accumulated.
+ */
+ pr_warn_once("KGSL: Abnormal value :0x%x (0x%x) from perf counter : 0x%x\n",
+ val, *counter, reg);
+ return 0;
+ }
}
*counter = val;
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 423071811b43..0e3e5b64bdc7 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1530,6 +1530,10 @@ static unsigned int a3xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
A3XX_UCHE_CACHE_INVALIDATE0_REG),
ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE1,
A3XX_UCHE_CACHE_INVALIDATE1_REG),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO,
+ A3XX_RBBM_PERFCTR_RBBM_0_LO),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI,
+ A3XX_RBBM_PERFCTR_RBBM_0_HI),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO,
A3XX_RBBM_PERFCTR_LOAD_VALUE_LO),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI,
diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c
index 5ca04e522270..6170cc263e4a 100644
--- a/drivers/gpu/msm/adreno_a4xx.c
+++ b/drivers/gpu/msm/adreno_a4xx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -806,6 +806,10 @@ static unsigned int a4xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SW_RESET_CMD, A4XX_RBBM_SW_RESET_CMD),
ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0, A4XX_UCHE_INVALIDATE0),
ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE1, A4XX_UCHE_INVALIDATE1),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO,
+ A4XX_RBBM_PERFCTR_RBBM_0_LO),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI,
+ A4XX_RBBM_PERFCTR_RBBM_0_HI),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO,
A4XX_RBBM_PERFCTR_LOAD_VALUE_LO),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI,
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 0715022be6e3..3fb13c7a0814 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -715,6 +715,10 @@ static int _load_gpmu_firmware(struct adreno_device *adreno_dev)
if (ret)
goto err;
+ /* Integer overflow check for cmd_size */
+ if (data[2] > (data[0] - 2))
+ goto err;
+
cmds = data + data[2] + 3;
cmd_size = data[0] - data[2] - 2;
@@ -2069,6 +2073,9 @@ static void a5xx_start(struct adreno_device *adreno_dev)
}
+ /* Disable All flat shading optimization */
+ kgsl_regrmw(device, A5XX_VPC_DBG_ECO_CNTL, 0, 0x1 << 10);
+
/*
* VPC corner case with local memory load kill leads to corrupt
* internal state. Normal Disable does not work for all a5x chips.
@@ -3069,6 +3076,10 @@ static unsigned int a5xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_BLOCK_SW_RESET_CMD2,
A5XX_RBBM_BLOCK_SW_RESET_CMD2),
ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0, A5XX_UCHE_INVALIDATE0),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO,
+ A5XX_RBBM_PERFCTR_RBBM_0_LO),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI,
+ A5XX_RBBM_PERFCTR_RBBM_0_HI),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO,
A5XX_RBBM_PERFCTR_LOAD_VALUE_LO),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI,
diff --git a/drivers/gpu/msm/adreno_a5xx_preempt.c b/drivers/gpu/msm/adreno_a5xx_preempt.c
index 0e56731b16e2..883a9810fbf4 100644
--- a/drivers/gpu/msm/adreno_a5xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a5xx_preempt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -537,13 +537,42 @@ static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev)
KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED,
"smmu_info");
}
+
+static void a5xx_preemption_iommu_close(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device);
+
+ kgsl_free_global(device, &iommu->smmu_info);
+}
+
#else
static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev)
{
return -ENODEV;
}
+
+static void a5xx_preemption_iommu_close(struct adreno_device *adreno_dev)
+{
+}
#endif
+static void a5xx_preemption_close(struct kgsl_device *device)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct adreno_preemption *preempt = &adreno_dev->preempt;
+ struct adreno_ringbuffer *rb;
+ unsigned int i;
+
+ del_timer(&preempt->timer);
+ kgsl_free_global(device, &preempt->counters);
+ a5xx_preemption_iommu_close(adreno_dev);
+
+ FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
+ kgsl_free_global(device, &rb->preemption_desc);
+ }
+}
+
int a5xx_preemption_init(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
@@ -568,7 +597,7 @@ int a5xx_preemption_init(struct adreno_device *adreno_dev)
A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE, 0, 0,
"preemption_counters");
if (ret)
- return ret;
+ goto err;
addr = preempt->counters.gpuaddr;
@@ -576,10 +605,16 @@ int a5xx_preemption_init(struct adreno_device *adreno_dev)
FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
ret = a5xx_preemption_ringbuffer_init(adreno_dev, rb, addr);
if (ret)
- return ret;
+ goto err;
addr += A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE;
}
- return a5xx_preemption_iommu_init(adreno_dev);
+ ret = a5xx_preemption_iommu_init(adreno_dev);
+
+err:
+ if (ret)
+ a5xx_preemption_close(device);
+
+ return ret;
}
diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c
index 5306303b8d15..2027ac66f737 100644
--- a/drivers/gpu/msm/adreno_debugfs.c
+++ b/drivers/gpu/msm/adreno_debugfs.c
@@ -131,6 +131,8 @@ typedef void (*reg_read_fill_t)(struct kgsl_device *device, int i,
static void sync_event_print(struct seq_file *s,
struct kgsl_drawobj_sync_event *sync_event)
{
+ unsigned long flags;
+
switch (sync_event->type) {
case KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP: {
seq_printf(s, "sync: ctx: %d ts: %d",
@@ -138,9 +140,13 @@ static void sync_event_print(struct seq_file *s,
break;
}
case KGSL_CMD_SYNCPOINT_TYPE_FENCE:
+ spin_lock_irqsave(&sync_event->handle_lock, flags);
+
seq_printf(s, "sync: [%pK] %s", sync_event->handle,
(sync_event->handle && sync_event->handle->fence)
? sync_event->handle->fence->name : "NULL");
+
+ spin_unlock_irqrestore(&sync_event->handle_lock, flags);
break;
default:
seq_printf(s, "sync: type: %d", sync_event->type);
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 8902c3175c79..1a94e71f5c1d 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -1460,7 +1460,9 @@ int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv,
spin_unlock(&drawctxt->lock);
- kgsl_pwrctrl_update_l2pc(&adreno_dev->dev);
+ if (device->pwrctrl.l2pc_update_queue)
+ kgsl_pwrctrl_update_l2pc(&adreno_dev->dev,
+ KGSL_L2PC_QUEUE_TIMEOUT);
/* Add the context to the dispatcher pending list */
dispatcher_queue_context(adreno_dev, drawctxt);
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index d79d9613043f..65e73356857f 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -26,6 +26,7 @@
#include "adreno_iommu.h"
#include "adreno_pm4types.h"
#include "adreno_ringbuffer.h"
+#include "adreno_trace.h"
#include "a3xx_reg.h"
#include "adreno_a5xx.h"
@@ -58,6 +59,7 @@ static void _cff_write_ringbuffer(struct adreno_ringbuffer *rb)
}
static void adreno_get_submit_time(struct adreno_device *adreno_dev,
+ struct adreno_ringbuffer *rb,
struct adreno_submit_time *time)
{
unsigned long flags;
@@ -87,6 +89,9 @@ static void adreno_get_submit_time(struct adreno_device *adreno_dev,
} else
time->ticks = 0;
+ /* Trace the GPU time to create a mapping to ftrace time */
+ trace_adreno_cmdbatch_sync(rb->drawctxt_active, time->ticks);
+
/* Get the kernel clock for time since boot */
time->ktime = local_clock();
@@ -128,7 +133,7 @@ void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb,
_cff_write_ringbuffer(rb);
if (time != NULL)
- adreno_get_submit_time(adreno_dev, time);
+ adreno_get_submit_time(adreno_dev, rb, time);
adreno_ringbuffer_wptr(adreno_dev, rb);
}
@@ -198,8 +203,9 @@ int adreno_ringbuffer_start(struct adreno_device *adreno_dev,
FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
kgsl_sharedmem_set(device, &(rb->buffer_desc),
0, 0xAA, KGSL_RB_SIZE);
- kgsl_sharedmem_writel(device, &device->scratch,
- SCRATCH_RPTR_OFFSET(rb->id), 0);
+ if (!adreno_is_a3xx(adreno_dev))
+ kgsl_sharedmem_writel(device, &device->scratch,
+ SCRATCH_RPTR_OFFSET(rb->id), 0);
rb->wptr = 0;
rb->_wptr = 0;
rb->wptr_preempt_end = 0xFFFFFFFF;
@@ -260,9 +266,16 @@ static int _adreno_ringbuffer_probe(struct adreno_device *adreno_dev,
int adreno_ringbuffer_probe(struct adreno_device *adreno_dev, bool nopreempt)
{
- int status = 0;
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
- int i;
+ int i, status;
+
+ if (!adreno_is_a3xx(adreno_dev)) {
+ status = kgsl_allocate_global(device, &device->scratch,
+ PAGE_SIZE, 0, 0, "scratch");
+ if (status != 0)
+ return status;
+ }
if (nopreempt == false && ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION))
adreno_dev->num_ringbuffers = gpudev->num_prio_levels;
@@ -298,9 +311,13 @@ static void _adreno_ringbuffer_close(struct adreno_device *adreno_dev,
void adreno_ringbuffer_close(struct adreno_device *adreno_dev)
{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct adreno_ringbuffer *rb;
int i;
+ if (!adreno_is_a3xx(adreno_dev))
+ kgsl_free_global(device, &device->scratch);
+
FOR_EACH_RINGBUFFER(adreno_dev, rb, i)
_adreno_ringbuffer_close(adreno_dev, rb);
}
@@ -484,12 +501,17 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb,
if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP)
total_sizedwords += 9;
- /* WAIT_MEM_WRITES - needed in the stall on fault case
- * to prevent out of order CP operations that can result
- * in a CACHE_FLUSH_TS interrupt storm */
- if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE,
+ /* Don't insert any commands if stall on fault is not supported. */
+ if ((ADRENO_GPUREV(adreno_dev) > 500) && !adreno_is_a510(adreno_dev)) {
+ /*
+ * WAIT_MEM_WRITES - needed in the stall on fault case
+ * to prevent out of order CP operations that can result
+ * in a CACHE_FLUSH_TS interrupt storm
+ */
+ if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE,
&adreno_dev->ft_pf_policy))
- total_sizedwords += 1;
+ total_sizedwords += 1;
+ }
ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords);
if (IS_ERR(ringcmds))
@@ -576,14 +598,18 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb,
if (profile_ready)
adreno_profile_postib_processing(adreno_dev, &flags, &ringcmds);
- /*
- * WAIT_MEM_WRITES - needed in the stall on fault case to prevent
- * out of order CP operations that can result in a CACHE_FLUSH_TS
- * interrupt storm
- */
- if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE,
+ /* Don't insert any commands if stall on fault is not supported. */
+ if ((ADRENO_GPUREV(adreno_dev) > 500) && !adreno_is_a510(adreno_dev)) {
+ /*
+ * WAIT_MEM_WRITES - needed in the stall on fault case
+ * to prevent out of order CP operations that can result
+ * in a CACHE_FLUSH_TS interrupt storm
+ */
+ if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE,
&adreno_dev->ft_pf_policy))
- *ringcmds++ = cp_packet(adreno_dev, CP_WAIT_MEM_WRITES, 0);
+ *ringcmds++ = cp_packet(adreno_dev,
+ CP_WAIT_MEM_WRITES, 0);
+ }
/*
* Do a unique memory write from the GPU. This can be used in
diff --git a/drivers/gpu/msm/adreno_trace.h b/drivers/gpu/msm/adreno_trace.h
index 16ca0980cfbe..74c4c4e6e1fa 100644
--- a/drivers/gpu/msm/adreno_trace.h
+++ b/drivers/gpu/msm/adreno_trace.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -148,6 +148,29 @@ TRACE_EVENT(adreno_cmdbatch_retired,
)
);
+TRACE_EVENT(adreno_cmdbatch_sync,
+ TP_PROTO(struct adreno_context *drawctxt,
+ uint64_t ticks),
+ TP_ARGS(drawctxt, ticks),
+ TP_STRUCT__entry(
+ __field(unsigned int, id)
+ __field(unsigned int, timestamp)
+ __field(uint64_t, ticks)
+ __field(int, prio)
+ ),
+ TP_fast_assign(
+ __entry->id = drawctxt->base.id;
+ __entry->timestamp = drawctxt->timestamp;
+ __entry->ticks = ticks;
+ __entry->prio = drawctxt->base.priority;
+ ),
+ TP_printk(
+ "ctx=%u ctx_prio=%d ts=%u ticks=%lld",
+ __entry->id, __entry->prio, __entry->timestamp,
+ __entry->ticks
+ )
+);
+
TRACE_EVENT(adreno_cmdbatch_fault,
TP_PROTO(struct kgsl_drawobj_cmd *cmdobj, unsigned int fault),
TP_ARGS(cmdobj, fault),
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index afb489f10172..28ee383014a6 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1120,8 +1120,6 @@ static int kgsl_open_device(struct kgsl_device *device)
atomic_inc(&device->active_cnt);
kgsl_sharedmem_set(device, &device->memstore, 0, 0,
device->memstore.size);
- kgsl_sharedmem_set(device, &device->scratch, 0, 0,
- device->scratch.size);
result = device->ftbl->init(device);
if (result)
@@ -3451,7 +3449,7 @@ static int _sparse_add_to_bind_tree(struct kgsl_mem_entry *entry,
struct sparse_bind_object *new;
struct rb_node **node, *parent = NULL;
- new = kzalloc(sizeof(*new), GFP_KERNEL);
+ new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (new == NULL)
return -ENOMEM;
@@ -3485,7 +3483,6 @@ static int _sparse_rm_from_bind_tree(struct kgsl_mem_entry *entry,
struct sparse_bind_object *obj,
uint64_t v_offset, uint64_t size)
{
- spin_lock(&entry->bind_lock);
if (v_offset == obj->v_off && size >= obj->size) {
/*
* We are all encompassing, remove the entry and free
@@ -3518,7 +3515,6 @@ static int _sparse_rm_from_bind_tree(struct kgsl_mem_entry *entry,
obj->size = v_offset - obj->v_off;
- spin_unlock(&entry->bind_lock);
ret = _sparse_add_to_bind_tree(entry, v_offset + size,
obj->p_memdesc,
obj->p_off + (v_offset - obj->v_off) + size,
@@ -3528,11 +3524,10 @@ static int _sparse_rm_from_bind_tree(struct kgsl_mem_entry *entry,
return ret;
}
- spin_unlock(&entry->bind_lock);
-
return 0;
}
+/* entry->bind_lock must be held by the caller */
static struct sparse_bind_object *_find_containing_bind_obj(
struct kgsl_mem_entry *entry,
uint64_t offset, uint64_t size)
@@ -3540,8 +3535,6 @@ static struct sparse_bind_object *_find_containing_bind_obj(
struct sparse_bind_object *obj = NULL;
struct rb_node *node = entry->bind_tree.rb_node;
- spin_lock(&entry->bind_lock);
-
while (node != NULL) {
obj = rb_entry(node, struct sparse_bind_object, node);
@@ -3560,33 +3553,16 @@ static struct sparse_bind_object *_find_containing_bind_obj(
}
}
- spin_unlock(&entry->bind_lock);
-
return obj;
}
+/* entry->bind_lock must be held by the caller */
static int _sparse_unbind(struct kgsl_mem_entry *entry,
struct sparse_bind_object *bind_obj,
uint64_t offset, uint64_t size)
{
- struct kgsl_memdesc *memdesc = bind_obj->p_memdesc;
- struct kgsl_pagetable *pt = memdesc->pagetable;
int ret;
- if (memdesc->cur_bindings < (size / PAGE_SIZE))
- return -EINVAL;
-
- memdesc->cur_bindings -= size / PAGE_SIZE;
-
- ret = kgsl_mmu_unmap_offset(pt, memdesc,
- entry->memdesc.gpuaddr, offset, size);
- if (ret)
- return ret;
-
- ret = kgsl_mmu_sparse_dummy_map(pt, &entry->memdesc, offset, size);
- if (ret)
- return ret;
-
ret = _sparse_rm_from_bind_tree(entry, bind_obj, offset, size);
if (ret == 0) {
atomic_long_sub(size, &kgsl_driver.stats.mapped);
@@ -3600,6 +3576,8 @@ static long sparse_unbind_range(struct kgsl_sparse_binding_object *obj,
struct kgsl_mem_entry *virt_entry)
{
struct sparse_bind_object *bind_obj;
+ struct kgsl_memdesc *memdesc;
+ struct kgsl_pagetable *pt;
int ret = 0;
uint64_t size = obj->size;
uint64_t tmp_size = obj->size;
@@ -3607,9 +3585,14 @@ static long sparse_unbind_range(struct kgsl_sparse_binding_object *obj,
while (size > 0 && ret == 0) {
tmp_size = size;
+
+ spin_lock(&virt_entry->bind_lock);
bind_obj = _find_containing_bind_obj(virt_entry, offset, size);
- if (bind_obj == NULL)
+
+ if (bind_obj == NULL) {
+ spin_unlock(&virt_entry->bind_lock);
return 0;
+ }
if (bind_obj->v_off > offset) {
tmp_size = size - bind_obj->v_off - offset;
@@ -3626,7 +3609,28 @@ static long sparse_unbind_range(struct kgsl_sparse_binding_object *obj,
tmp_size = bind_obj->size;
}
+ memdesc = bind_obj->p_memdesc;
+ pt = memdesc->pagetable;
+
+ if (memdesc->cur_bindings < (tmp_size / PAGE_SIZE)) {
+ spin_unlock(&virt_entry->bind_lock);
+ return -EINVAL;
+ }
+
+ memdesc->cur_bindings -= tmp_size / PAGE_SIZE;
+
ret = _sparse_unbind(virt_entry, bind_obj, offset, tmp_size);
+ spin_unlock(&virt_entry->bind_lock);
+
+ ret = kgsl_mmu_unmap_offset(pt, memdesc,
+ virt_entry->memdesc.gpuaddr, offset, tmp_size);
+ if (ret)
+ return ret;
+
+ ret = kgsl_mmu_sparse_dummy_map(pt, memdesc, offset, tmp_size);
+ if (ret)
+ return ret;
+
if (ret == 0) {
offset += tmp_size;
size -= tmp_size;
@@ -4399,13 +4403,13 @@ kgsl_get_unmapped_area(struct file *file, unsigned long addr,
if (!kgsl_memdesc_use_cpu_map(&entry->memdesc)) {
val = get_unmapped_area(NULL, addr, len, 0, flags);
if (IS_ERR_VALUE(val))
- KGSL_MEM_ERR(device,
+ KGSL_DRV_ERR_RATELIMIT(device,
"get_unmapped_area: pid %d addr %lx pgoff %lx len %ld failed error %d\n",
private->pid, addr, pgoff, len, (int) val);
} else {
val = _get_svm_area(private, entry, addr, len, flags);
if (IS_ERR_VALUE(val))
- KGSL_MEM_ERR(device,
+ KGSL_DRV_ERR_RATELIMIT(device,
"_get_svm_area: pid %d mmap_base %lx addr %lx pgoff %lx len %ld failed error %d\n",
private->pid, current->mm->mmap_base, addr,
pgoff, len, (int) val);
@@ -4724,11 +4728,6 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
if (status != 0)
goto error_close_mmu;
- status = kgsl_allocate_global(device, &device->scratch,
- PAGE_SIZE, 0, 0, "scratch");
- if (status != 0)
- goto error_free_memstore;
-
/*
* The default request type PM_QOS_REQ_ALL_CORES is
* applicable to all CPU cores that are online and
@@ -4774,8 +4773,6 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
return 0;
-error_free_memstore:
- kgsl_free_global(device, &device->memstore);
error_close_mmu:
kgsl_mmu_close(device);
error_pwrctrl_close:
@@ -4803,8 +4800,6 @@ void kgsl_device_platform_remove(struct kgsl_device *device)
idr_destroy(&device->context_idr);
- kgsl_free_global(device, &device->scratch);
-
kgsl_free_global(device, &device->memstore);
kgsl_mmu_close(device);
diff --git a/drivers/gpu/msm/kgsl_drawobj.c b/drivers/gpu/msm/kgsl_drawobj.c
index f8f0e7ccb0d3..fba18231cb72 100644
--- a/drivers/gpu/msm/kgsl_drawobj.c
+++ b/drivers/gpu/msm/kgsl_drawobj.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -79,6 +79,7 @@ void kgsl_dump_syncpoints(struct kgsl_device *device,
{
struct kgsl_drawobj_sync_event *event;
unsigned int i;
+ unsigned long flags;
for (i = 0; i < syncobj->numsyncs; i++) {
event = &syncobj->synclist[i];
@@ -101,12 +102,16 @@ void kgsl_dump_syncpoints(struct kgsl_device *device,
break;
}
case KGSL_CMD_SYNCPOINT_TYPE_FENCE:
+ spin_lock_irqsave(&event->handle_lock, flags);
+
if (event->handle)
dev_err(device->dev, " fence: [%pK] %s\n",
event->handle->fence,
event->handle->name);
else
dev_err(device->dev, " fence: invalid\n");
+
+ spin_unlock_irqrestore(&event->handle_lock, flags);
break;
}
}
@@ -119,6 +124,7 @@ static void syncobj_timer(unsigned long data)
struct kgsl_drawobj *drawobj = DRAWOBJ(syncobj);
struct kgsl_drawobj_sync_event *event;
unsigned int i;
+ unsigned long flags;
if (syncobj == NULL || drawobj->context == NULL)
return;
@@ -147,12 +153,16 @@ static void syncobj_timer(unsigned long data)
i, event->context->id, event->timestamp);
break;
case KGSL_CMD_SYNCPOINT_TYPE_FENCE:
+ spin_lock_irqsave(&event->handle_lock, flags);
+
if (event->handle != NULL) {
dev_err(device->dev, " [%d] FENCE %s\n",
i, event->handle->fence ?
event->handle->fence->name : "NULL");
kgsl_sync_fence_log(event->handle->fence);
}
+
+ spin_unlock_irqrestore(&event->handle_lock, flags);
break;
}
}
@@ -231,7 +241,7 @@ static void drawobj_destroy_sparse(struct kgsl_drawobj *drawobj)
static void drawobj_destroy_sync(struct kgsl_drawobj *drawobj)
{
struct kgsl_drawobj_sync *syncobj = SYNCOBJ(drawobj);
- unsigned long pending;
+ unsigned long pending, flags;
unsigned int i;
/* Zap the canary timer */
@@ -262,8 +272,17 @@ static void drawobj_destroy_sync(struct kgsl_drawobj *drawobj)
drawobj_sync_func, event);
break;
case KGSL_CMD_SYNCPOINT_TYPE_FENCE:
- if (kgsl_sync_fence_async_cancel(event->handle))
+ spin_lock_irqsave(&event->handle_lock, flags);
+
+ if (kgsl_sync_fence_async_cancel(event->handle)) {
+ event->handle = NULL;
+ spin_unlock_irqrestore(
+ &event->handle_lock, flags);
drawobj_put(drawobj);
+ } else {
+ spin_unlock_irqrestore(
+ &event->handle_lock, flags);
+ }
break;
}
}
@@ -325,12 +344,23 @@ EXPORT_SYMBOL(kgsl_drawobj_destroy);
static void drawobj_sync_fence_func(void *priv)
{
+ unsigned long flags;
struct kgsl_drawobj_sync_event *event = priv;
+ drawobj_sync_expire(event->device, event);
+
trace_syncpoint_fence_expire(event->syncobj,
event->handle ? event->handle->name : "unknown");
- drawobj_sync_expire(event->device, event);
+ spin_lock_irqsave(&event->handle_lock, flags);
+
+ /*
+ * Setting the event->handle to NULL here make sure that
+ * other function does not dereference a invalid pointer.
+ */
+ event->handle = NULL;
+
+ spin_unlock_irqrestore(&event->handle_lock, flags);
drawobj_put(&event->syncobj->base);
}
@@ -348,7 +378,14 @@ static int drawobj_add_sync_fence(struct kgsl_device *device,
struct kgsl_cmd_syncpoint_fence *sync = priv;
struct kgsl_drawobj *drawobj = DRAWOBJ(syncobj);
struct kgsl_drawobj_sync_event *event;
+ struct sync_fence *fence = NULL;
unsigned int id;
+ unsigned long flags;
+ int ret = 0;
+
+ fence = sync_fence_fdget(sync->fd);
+ if (fence == NULL)
+ return -EINVAL;
kref_get(&drawobj->refcount);
@@ -362,32 +399,39 @@ static int drawobj_add_sync_fence(struct kgsl_device *device,
event->device = device;
event->context = NULL;
+ spin_lock_init(&event->handle_lock);
set_bit(event->id, &syncobj->pending);
+ trace_syncpoint_fence(syncobj, fence->name);
+
+ spin_lock_irqsave(&event->handle_lock, flags);
+
event->handle = kgsl_sync_fence_async_wait(sync->fd,
drawobj_sync_fence_func, event);
if (IS_ERR_OR_NULL(event->handle)) {
- int ret = PTR_ERR(event->handle);
+ ret = PTR_ERR(event->handle);
- clear_bit(event->id, &syncobj->pending);
event->handle = NULL;
+ spin_unlock_irqrestore(&event->handle_lock, flags);
+
+ clear_bit(event->id, &syncobj->pending);
drawobj_put(drawobj);
/*
- * If ret == 0 the fence was already signaled - print a trace
- * message so we can track that
+ * Print a syncpoint_fence_expire trace if
+ * the fence is already signaled or there is
+ * a failure in registering the fence waiter.
*/
- if (ret == 0)
- trace_syncpoint_fence_expire(syncobj, "signaled");
-
- return ret;
+ trace_syncpoint_fence_expire(syncobj, (ret < 0) ?
+ "error" : fence->name);
+ } else {
+ spin_unlock_irqrestore(&event->handle_lock, flags);
}
- trace_syncpoint_fence(syncobj, event->handle->name);
-
- return 0;
+ sync_fence_put(fence);
+ return ret;
}
/* drawobj_add_sync_timestamp() - Add a new sync point for a sync obj
diff --git a/drivers/gpu/msm/kgsl_drawobj.h b/drivers/gpu/msm/kgsl_drawobj.h
index fd9d2bc93f41..b208870e4c42 100644
--- a/drivers/gpu/msm/kgsl_drawobj.h
+++ b/drivers/gpu/msm/kgsl_drawobj.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -114,6 +114,7 @@ struct kgsl_drawobj_sync {
* register this event
* @timestamp: Pending timestamp for the event
* @handle: Pointer to a sync fence handle
+ * @handle_lock: Spin lock to protect handle
* @device: Pointer to the KGSL device
*/
struct kgsl_drawobj_sync_event {
@@ -123,6 +124,7 @@ struct kgsl_drawobj_sync_event {
struct kgsl_context *context;
unsigned int timestamp;
struct kgsl_sync_fence_waiter *handle;
+ spinlock_t handle_lock;
struct kgsl_device *device;
};
diff --git a/drivers/gpu/msm/kgsl_log.h b/drivers/gpu/msm/kgsl_log.h
index 51baabefb6d3..9b7833bdb2df 100644
--- a/drivers/gpu/msm/kgsl_log.h
+++ b/drivers/gpu/msm/kgsl_log.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2008-2011,2013-2014,2016 The Linux Foundation.
+/* Copyright (c) 2002,2008-2011,2013-2014,2016-2017 The Linux Foundation.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -67,6 +67,13 @@
__func__, ##args);\
} while (0)
+#define KGSL_LOG_ERR_RATELIMITED(dev, lvl, fmt, args...) \
+ do { \
+ if ((lvl) >= 3) \
+ dev_err_ratelimited(dev, "|%s| " fmt, \
+ __func__, ##args);\
+ } while (0)
+
#define KGSL_DRV_INFO(_dev, fmt, args...) \
KGSL_LOG_INFO(_dev->dev, _dev->drv_log, fmt, ##args)
#define KGSL_DRV_WARN(_dev, fmt, args...) \
@@ -77,6 +84,8 @@ KGSL_LOG_ERR(_dev->dev, _dev->drv_log, fmt, ##args)
KGSL_LOG_CRIT(_dev->dev, _dev->drv_log, fmt, ##args)
#define KGSL_DRV_CRIT_RATELIMIT(_dev, fmt, args...) \
KGSL_LOG_CRIT_RATELIMITED(_dev->dev, _dev->drv_log, fmt, ##args)
+#define KGSL_DRV_ERR_RATELIMIT(_dev, fmt, args...) \
+KGSL_LOG_ERR_RATELIMITED(_dev->dev, _dev->drv_log, fmt, ##args)
#define KGSL_DRV_FATAL(_dev, fmt, args...) \
KGSL_LOG_FATAL((_dev)->dev, (_dev)->drv_log, fmt, ##args)
diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c
index c31a85b07447..4a9997b02155 100644
--- a/drivers/gpu/msm/kgsl_pool.c
+++ b/drivers/gpu/msm/kgsl_pool.c
@@ -65,26 +65,19 @@ _kgsl_get_pool_from_order(unsigned int order)
/* Map the page into kernel and zero it out */
static void
-_kgsl_pool_zero_page(struct page *p, unsigned int pool_order)
+_kgsl_pool_zero_page(struct page *p)
{
- int i;
-
- for (i = 0; i < (1 << pool_order); i++) {
- struct page *page = nth_page(p, i);
- void *addr = kmap_atomic(page);
+ void *addr = kmap_atomic(p);
- memset(addr, 0, PAGE_SIZE);
- dmac_flush_range(addr, addr + PAGE_SIZE);
- kunmap_atomic(addr);
- }
+ memset(addr, 0, PAGE_SIZE);
+ dmac_flush_range(addr, addr + PAGE_SIZE);
+ kunmap_atomic(addr);
}
/* Add a page to specified pool */
static void
_kgsl_pool_add_page(struct kgsl_page_pool *pool, struct page *p)
{
- _kgsl_pool_zero_page(p, pool->pool_order);
-
spin_lock(&pool->list_lock);
list_add_tail(&p->lru, &pool->page_list);
pool->page_count++;
@@ -329,7 +322,6 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages,
} else
return -ENOMEM;
}
- _kgsl_pool_zero_page(page, order);
goto done;
}
@@ -349,7 +341,6 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages,
page = alloc_pages(gfp_mask, order);
if (page == NULL)
return -ENOMEM;
- _kgsl_pool_zero_page(page, order);
goto done;
}
}
@@ -379,13 +370,12 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages,
} else
return -ENOMEM;
}
-
- _kgsl_pool_zero_page(page, order);
}
done:
for (j = 0; j < (*page_size >> PAGE_SHIFT); j++) {
p = nth_page(page, j);
+ _kgsl_pool_zero_page(p);
pages[pcount] = p;
pcount++;
}
@@ -422,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_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index ad8b0131bb46..8c998a5d791b 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -43,13 +43,6 @@
#define DEFAULT_BUS_P 25
-/*
- * The effective duration of qos request in usecs. After
- * timeout, qos request is cancelled automatically.
- * Kept 80ms default, inline with default GPU idle time.
- */
-#define KGSL_L2PC_CPU_TIMEOUT (80 * 1000)
-
/* Order deeply matters here because reasons. New entries go on the end */
static const char * const clocks[] = {
"src_clk",
@@ -520,12 +513,14 @@ EXPORT_SYMBOL(kgsl_pwrctrl_set_constraint);
/**
* kgsl_pwrctrl_update_l2pc() - Update existing qos request
* @device: Pointer to the kgsl_device struct
+ * @timeout_us: the effective duration of qos request in usecs.
*
* Updates an existing qos request to avoid L2PC on the
* CPUs (which are selected through dtsi) on which GPU
* thread is running. This would help for performance.
*/
-void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device)
+void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device,
+ unsigned long timeout_us)
{
int cpu;
@@ -539,7 +534,7 @@ void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device)
pm_qos_update_request_timeout(
&device->pwrctrl.l2pc_cpus_qos,
device->pwrctrl.pm_qos_cpu_mask_latency,
- KGSL_L2PC_CPU_TIMEOUT);
+ timeout_us);
}
}
EXPORT_SYMBOL(kgsl_pwrctrl_update_l2pc);
@@ -2201,6 +2196,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
kgsl_property_read_u32(device, "qcom,l2pc-cpu-mask",
&pwr->l2pc_cpus_mask);
+ pwr->l2pc_update_queue = of_property_read_bool(
+ device->pdev->dev.of_node,
+ "qcom,l2pc-update-queue");
+
pm_runtime_enable(&pdev->dev);
ocmem_bus_node = of_find_node_by_name(
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 42f918b80fcd..02707c901839 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -51,6 +51,19 @@
#define KGSL_PWR_DEL_LIMIT 1
#define KGSL_PWR_SET_LIMIT 2
+/*
+ * The effective duration of qos request in usecs at queue time.
+ * After timeout, qos request is cancelled automatically.
+ * Kept 80ms default, inline with default GPU idle time.
+ */
+#define KGSL_L2PC_QUEUE_TIMEOUT (80 * 1000)
+
+/*
+ * The effective duration of qos request in usecs at wakeup time.
+ * After timeout, qos request is cancelled automatically.
+ */
+#define KGSL_L2PC_WAKEUP_TIMEOUT (10 * 1000)
+
enum kgsl_pwrctrl_timer_type {
KGSL_PWR_IDLE_TIMER,
};
@@ -128,10 +141,12 @@ struct kgsl_regulator {
* @irq_name - resource name for the IRQ
* @clk_stats - structure of clock statistics
* @l2pc_cpus_mask - mask to avoid L2PC on masked CPUs
+ * @l2pc_update_queue - Boolean flag to avoid L2PC on masked CPUs at queue time
* @l2pc_cpus_qos - qos structure to avoid L2PC on CPUs
* @pm_qos_req_dma - the power management quality of service structure
* @pm_qos_active_latency - allowed CPU latency in microseconds when active
* @pm_qos_cpu_mask_latency - allowed CPU mask latency in microseconds
+ * @input_disable - To disable GPU wakeup on touch input event
* @pm_qos_wakeup_latency - allowed CPU latency in microseconds during wakeup
* @bus_control - true if the bus calculation is independent
* @bus_mod - modifier from the current power level for the bus vote
@@ -183,11 +198,13 @@ struct kgsl_pwrctrl {
const char *irq_name;
struct kgsl_clk_stats clk_stats;
unsigned int l2pc_cpus_mask;
+ bool l2pc_update_queue;
struct pm_qos_request l2pc_cpus_qos;
struct pm_qos_request pm_qos_req_dma;
unsigned int pm_qos_active_latency;
unsigned int pm_qos_cpu_mask_latency;
unsigned int pm_qos_wakeup_latency;
+ bool input_disable;
bool bus_control;
int bus_mod;
unsigned int bus_percent_ab;
@@ -249,5 +266,6 @@ int kgsl_active_count_wait(struct kgsl_device *device, int count);
void kgsl_pwrctrl_busy_time(struct kgsl_device *device, u64 time, u64 busy);
void kgsl_pwrctrl_set_constraint(struct kgsl_device *device,
struct kgsl_pwr_constraint *pwrc, uint32_t id);
-void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device);
+void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device,
+ unsigned long timeout_us);
#endif /* __KGSL_PWRCTRL_H */
diff --git a/drivers/gpu/msm/kgsl_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/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c
index 358b3b038899..4bf591c236a7 100644
--- a/drivers/gpu/msm/kgsl_sync.c
+++ b/drivers/gpu/msm/kgsl_sync.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -179,7 +179,7 @@ int kgsl_add_fence_event(struct kgsl_device *device,
goto out;
}
snprintf(fence_name, sizeof(fence_name),
- "%s-pid-%d-ctx-%d-ts-%d",
+ "%s-pid-%d-ctx-%d-ts-%u",
device->name, current->group_leader->pid,
context_id, timestamp);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 34ea83d067af..13dc2731195b 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2428,6 +2428,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PETZL, USB_DEVICE_ID_PETZL_HEADLAMP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
#if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index c7f8b70d15ee..37cbc2ecfc5f 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -777,6 +777,9 @@
#define USB_VENDOR_ID_PETALYNX 0x18b1
#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037
+#define USB_VENDOR_ID_PETZL 0x2122
+#define USB_DEVICE_ID_PETZL_HEADLAMP 0x1234
+
#define USB_VENDOR_ID_PHILIPS 0x0471
#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 6b00061c3746..a2ae2213ef3e 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -294,7 +294,7 @@ static void dw_i2c_plat_complete(struct device *dev)
#endif
#ifdef CONFIG_PM
-static int dw_i2c_plat_suspend(struct device *dev)
+static int dw_i2c_plat_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
@@ -318,11 +318,21 @@ static int dw_i2c_plat_resume(struct device *dev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int dw_i2c_plat_suspend(struct device *dev)
+{
+ pm_runtime_resume(dev);
+ return dw_i2c_plat_runtime_suspend(dev);
+}
+#endif
+
static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
.prepare = dw_i2c_plat_prepare,
.complete = dw_i2c_plat_complete,
SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
- SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
+ SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend,
+ dw_i2c_plat_resume,
+ NULL)
};
#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index fa24d5196615..c7122919a8c0 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -194,7 +194,6 @@ struct bmc150_accel_data {
struct device *dev;
int irq;
struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS];
- atomic_t active_intr;
struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS];
struct mutex mutex;
u8 fifo_mode, watermark;
@@ -489,11 +488,6 @@ static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
goto out_fix_power_state;
}
- if (state)
- atomic_inc(&data->active_intr);
- else
- atomic_dec(&data->active_intr);
-
return 0;
out_fix_power_state:
@@ -1704,8 +1698,7 @@ static int bmc150_accel_resume(struct device *dev)
struct bmc150_accel_data *data = iio_priv(indio_dev);
mutex_lock(&data->mutex);
- if (atomic_read(&data->active_intr))
- bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
+ bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
bmc150_accel_fifo_set_mode(data);
mutex_unlock(&data->mutex);
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index 5c20970ccbbb..28ab4e52dab5 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -180,6 +180,9 @@
#define FG_ADC_RR_VOLT_INPUT_FACTOR 8
#define FG_ADC_RR_CURR_INPUT_FACTOR 2000
#define FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL 1886
+#define FG_ADC_RR_CURR_USBIN_660_FACTOR_MIL 9
+#define FG_ADC_RR_CURR_USBIN_660_UV_VAL 579500
+
#define FG_ADC_SCALE_MILLI_FACTOR 1000
#define FG_ADC_KELVINMIL_CELSIUSMIL 273150
@@ -192,6 +195,9 @@
#define FG_RR_CONV_CONTINUOUS_TIME_MIN_US 50000
#define FG_RR_CONV_CONTINUOUS_TIME_MAX_US 51000
#define FG_RR_CONV_MAX_RETRY_CNT 50
+#define FG_RR_TP_REV_VERSION1 21
+#define FG_RR_TP_REV_VERSION2 29
+#define FG_RR_TP_REV_VERSION3 32
/*
* The channel number is not a physical index in hardware,
@@ -228,6 +234,7 @@ struct rradc_chip {
struct rradc_chan_prop *chan_props;
struct device_node *revid_dev_node;
struct pmic_revid_data *pmic_fab_id;
+ int volt;
};
struct rradc_channels {
@@ -353,7 +360,7 @@ static int rradc_post_process_volt(struct rradc_chip *chip,
return 0;
}
-static int rradc_post_process_curr(struct rradc_chip *chip,
+static int rradc_post_process_usbin_curr(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_ua)
{
@@ -361,11 +368,33 @@ static int rradc_post_process_curr(struct rradc_chip *chip,
if (!prop)
return -EINVAL;
-
- if (prop->channel == RR_ADC_USBIN_I)
- scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL;
- else
- scale = FG_ADC_RR_CURR_INPUT_FACTOR;
+ if (chip->revid_dev_node) {
+ switch (chip->pmic_fab_id->pmic_subtype) {
+ case PM660_SUBTYPE:
+ if (((chip->pmic_fab_id->tp_rev
+ >= FG_RR_TP_REV_VERSION1)
+ && (chip->pmic_fab_id->tp_rev
+ <= FG_RR_TP_REV_VERSION2))
+ || (chip->pmic_fab_id->tp_rev
+ >= FG_RR_TP_REV_VERSION3)) {
+ chip->volt = div64_s64(chip->volt, 1000);
+ chip->volt = chip->volt *
+ FG_ADC_RR_CURR_USBIN_660_FACTOR_MIL;
+ chip->volt = FG_ADC_RR_CURR_USBIN_660_UV_VAL -
+ (chip->volt);
+ chip->volt = div64_s64(1000000000, chip->volt);
+ scale = chip->volt;
+ } else
+ scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL;
+ break;
+ case PMI8998_SUBTYPE:
+ scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL;
+ break;
+ default:
+ pr_err("No PMIC subtype found\n");
+ return -EINVAL;
+ }
+ }
/* scale * V/A; 2.5V ADC full scale */
ua = ((int64_t)adc_code * scale);
@@ -376,6 +405,24 @@ static int rradc_post_process_curr(struct rradc_chip *chip,
return 0;
}
+static int rradc_post_process_dcin_curr(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_ua)
+{
+ int64_t ua = 0;
+
+ if (!prop)
+ return -EINVAL;
+
+ /* 0.5 V/A; 2.5V ADC full scale */
+ ua = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR);
+ ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR);
+ ua = div64_s64(ua, (FG_MAX_ADC_READINGS * 1000));
+ *result_ua = ua;
+
+ return 0;
+}
+
static int rradc_post_process_die_temp(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_millidegc)
@@ -591,13 +638,13 @@ static const struct rradc_channels rradc_chans[] = {
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB,
FG_ADC_RR_AUX_THERM_STS)
- RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr,
+ RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_usbin_curr,
FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB,
FG_ADC_RR_USB_IN_I_STS)
RR_ADC_CHAN_VOLT("usbin_v", &rradc_post_process_volt,
FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB,
FG_ADC_RR_USB_IN_V_STS)
- RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_curr,
+ RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_dcin_curr,
FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB,
FG_ADC_RR_DC_IN_I_STS)
RR_ADC_CHAN_VOLT("dcin_v", &rradc_post_process_volt,
@@ -955,6 +1002,21 @@ static int rradc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
+ if (((chip->pmic_fab_id->tp_rev
+ >= FG_RR_TP_REV_VERSION1)
+ && (chip->pmic_fab_id->tp_rev
+ <= FG_RR_TP_REV_VERSION2))
+ || (chip->pmic_fab_id->tp_rev
+ >= FG_RR_TP_REV_VERSION3)) {
+ if (chan->address == RR_ADC_USBIN_I) {
+ prop = &chip->chan_props[RR_ADC_USBIN_V];
+ rc = rradc_do_conversion(chip, prop, &adc_code);
+ if (rc)
+ break;
+ prop->scale(chip, prop, adc_code, &chip->volt);
+ }
+ }
+
prop = &chip->chan_props[chan->address];
rc = rradc_do_conversion(chip, prop, &adc_code);
if (rc)
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index b10f629cc44b..1dbc2143cdfc 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -77,7 +77,7 @@
#define VF610_ADC_ADSTS_MASK 0x300
#define VF610_ADC_ADLPC_EN 0x80
#define VF610_ADC_ADHSC_EN 0x400
-#define VF610_ADC_REFSEL_VALT 0x100
+#define VF610_ADC_REFSEL_VALT 0x800
#define VF610_ADC_REFSEL_VBG 0x1000
#define VF610_ADC_ADTRG_HARD 0x2000
#define VF610_ADC_AVGS_8 0x4000
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index 0a86ef43e781..a8db38db622e 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -36,8 +36,6 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
s32 poll_value = 0;
if (state) {
- if (!atomic_read(&st->user_requested_state))
- return 0;
if (sensor_hub_device_open(st->hsdev))
return -EIO;
@@ -84,6 +82,9 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
&report_val);
}
+ pr_debug("HID_SENSOR %s set power_state %d report_state %d\n",
+ st->pdev->name, state_val, report_val);
+
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
sizeof(state_val), &state_val);
@@ -107,6 +108,7 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
ret = pm_runtime_get_sync(&st->pdev->dev);
else {
pm_runtime_mark_last_busy(&st->pdev->dev);
+ pm_runtime_use_autosuspend(&st->pdev->dev);
ret = pm_runtime_put_autosuspend(&st->pdev->dev);
}
if (ret < 0) {
@@ -175,8 +177,6 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
/* Default to 3 seconds, but can be changed from sysfs */
pm_runtime_set_autosuspend_delay(&attrb->pdev->dev,
3000);
- pm_runtime_use_autosuspend(&attrb->pdev->dev);
-
return ret;
error_unreg_trigger:
iio_trigger_unregister(trig);
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index 2485b88ee1b6..1880105cc8c4 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -696,7 +696,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
.gyro_max_scale = 450,
.accel_max_val = IIO_M_S_2_TO_G(12500),
- .accel_max_scale = 5,
+ .accel_max_scale = 10,
},
[ADIS16485] = {
.channels = adis16485_channels,
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
index 12731d6b89ec..ec1b2e798cc1 100644
--- a/drivers/iio/light/tsl2563.c
+++ b/drivers/iio/light/tsl2563.c
@@ -626,7 +626,7 @@ static irqreturn_t tsl2563_event_handler(int irq, void *private)
struct tsl2563_chip *chip = iio_priv(dev_info);
iio_push_event(dev_info,
- IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+ IIO_UNMOD_EVENT_CODE(IIO_INTENSITY,
0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_EITHER),
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 3f5741a3e728..43d5166db4c6 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -857,6 +857,8 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
} else
ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr,
qp_attr_mask);
+ qp_attr->port_num = id_priv->id.port_num;
+ *qp_attr_mask |= IB_QP_PORT;
} else
ret = -ENOSYS;
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 1c02deab068f..b7a73f1a8beb 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -2287,6 +2287,11 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
+ if ((cmd.attr_mask & IB_QP_PORT) &&
+ (cmd.port_num < rdma_start_port(ib_dev) ||
+ cmd.port_num > rdma_end_port(ib_dev)))
+ return -EINVAL;
+
INIT_UDATA(&udata, buf + sizeof cmd, NULL, in_len - sizeof cmd,
out_len);
@@ -2827,6 +2832,10 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
+ if (cmd.attr.port_num < rdma_start_port(ib_dev) ||
+ cmd.attr.port_num > rdma_end_port(ib_dev))
+ return -EINVAL;
+
uobj = kmalloc(sizeof *uobj, GFP_KERNEL);
if (!uobj)
return -ENOMEM;
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index b0edb66a291b..0b7f5a701c60 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -1581,7 +1581,7 @@ isert_rcv_completion(struct iser_rx_desc *desc,
struct isert_conn *isert_conn,
u32 xfer_len)
{
- struct ib_device *ib_dev = isert_conn->cm_id->device;
+ struct ib_device *ib_dev = isert_conn->device->ib_device;
struct iscsi_hdr *hdr;
u64 rx_dma;
int rx_buflen;
diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c
index 56f2732334db..f85556079d12 100644
--- a/drivers/input/misc/hbtp_input.c
+++ b/drivers/input/misc/hbtp_input.c
@@ -98,9 +98,10 @@ static struct hbtp_data *hbtp;
static struct kobject *sensor_kobject;
#if defined(CONFIG_FB)
+static int hbtp_fb_early_suspend(struct hbtp_data *ts);
static int hbtp_fb_suspend(struct hbtp_data *ts);
static int hbtp_fb_early_resume(struct hbtp_data *ts);
-static int hbtp_fb_resume(struct hbtp_data *ts);
+static int hbtp_fb_revert_resume(struct hbtp_data *ts);
#endif
#if defined(CONFIG_FB)
@@ -145,6 +146,7 @@ static int fb_notifier_callback(struct notifier_block *self,
lcd_state <= FB_BLANK_NORMAL) {
pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n",
__func__);
+ hbtp_fb_early_suspend(hbtp_data);
} else {
pr_debug("%s: receives EARLY_BLANK:%d in %d state\n",
__func__, blank, lcd_state);
@@ -153,10 +155,12 @@ static int fb_notifier_callback(struct notifier_block *self,
if (blank <= FB_BLANK_NORMAL) {
pr_debug("%s: receives R_EARLY_BALNK:UNBLANK\n",
__func__);
+ hbtp_fb_early_suspend(hbtp_data);
hbtp_fb_suspend(hbtp_data);
} else if (blank == FB_BLANK_POWERDOWN) {
pr_debug("%s: receives R_EARLY_BALNK:POWERDOWN\n",
__func__);
+ hbtp_fb_revert_resume(hbtp_data);
} else {
pr_debug("%s: receives R_EARLY_BALNK:%d in %d state\n",
__func__, blank, lcd_state);
@@ -175,7 +179,6 @@ static int fb_notifier_callback(struct notifier_block *self,
} else if (blank <= FB_BLANK_NORMAL &&
lcd_state == FB_BLANK_POWERDOWN) {
pr_debug("%s: receives BLANK:UNBLANK\n", __func__);
- hbtp_fb_resume(hbtp_data);
} else {
pr_debug("%s: receives BLANK:%d in %d state\n",
__func__, blank, lcd_state);
@@ -1192,6 +1195,43 @@ error:
return rc;
}
+static int hbtp_fb_early_suspend(struct hbtp_data *ts)
+{
+ int rc = 0;
+ char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
+
+ mutex_lock(&hbtp->mutex);
+ if (ts->pdev && (!ts->power_sync_enabled)) {
+ pr_debug("%s: power_sync is not enabled\n", __func__);
+
+ if (ts->input_dev) {
+ kobject_uevent_env(&ts->input_dev->dev.kobj,
+ KOBJ_OFFLINE, envp);
+
+ if (ts->power_sig_enabled) {
+ pr_debug("%s: power_sig is enabled, wait for signal\n",
+ __func__);
+ mutex_unlock(&hbtp->mutex);
+ rc = wait_for_completion_interruptible(
+ &hbtp->power_suspend_sig);
+ if (rc != 0) {
+ pr_err("%s: wait for early suspend is interrupted\n",
+ __func__);
+ }
+ mutex_lock(&hbtp->mutex);
+ pr_debug("%s: Wait is done for early suspend\n",
+ __func__);
+ } else {
+ pr_debug("%s: power_sig is NOT enabled",
+ __func__);
+ }
+ }
+ }
+
+ mutex_unlock(&hbtp->mutex);
+ return rc;
+}
+
static int hbtp_fb_suspend(struct hbtp_data *ts)
{
int rc;
@@ -1217,26 +1257,28 @@ static int hbtp_fb_suspend(struct hbtp_data *ts)
goto err_power_disable;
}
ts->power_suspended = true;
- }
- if (ts->input_dev) {
- kobject_uevent_env(&ts->input_dev->dev.kobj,
- KOBJ_OFFLINE, envp);
+ if (ts->input_dev) {
+ kobject_uevent_env(&ts->input_dev->dev.kobj,
+ KOBJ_OFFLINE, envp);
- if (ts->power_sig_enabled) {
- pr_debug("%s: power_sig is enabled, wait for signal\n",
- __func__);
- mutex_unlock(&hbtp->mutex);
- rc = wait_for_completion_interruptible(
- &hbtp->power_suspend_sig);
- if (rc != 0) {
- pr_err("%s: wait for suspend is interrupted\n",
- __func__);
+ if (ts->power_sig_enabled) {
+ pr_debug("%s: power_sig is enabled, wait for signal\n",
+ __func__);
+ mutex_unlock(&hbtp->mutex);
+ rc = wait_for_completion_interruptible(
+ &hbtp->power_suspend_sig);
+ if (rc != 0) {
+ pr_err("%s: wait for suspend is interrupted\n",
+ __func__);
+ }
+ mutex_lock(&hbtp->mutex);
+ pr_debug("%s: Wait is done for suspend\n",
+ __func__);
+ } else {
+ pr_debug("%s: power_sig is NOT enabled",
+ __func__);
}
- mutex_lock(&hbtp->mutex);
- pr_debug("%s: Wait is done for suspend\n", __func__);
- } else {
- pr_debug("%s: power_sig is NOT enabled", __func__);
}
}
@@ -1278,39 +1320,40 @@ static int hbtp_fb_early_resume(struct hbtp_data *ts)
goto err_pin_enable;
}
+ if (ts->fb_resume_seq_delay) {
+ usleep_range(ts->fb_resume_seq_delay,
+ ts->fb_resume_seq_delay +
+ HBTP_HOLD_DURATION_US);
+ pr_debug("%s: fb_resume_seq_delay = %u\n",
+ __func__, ts->fb_resume_seq_delay);
+ }
+
ts->power_suspended = false;
+ }
- if (ts->input_dev) {
+ if (ts->input_dev) {
- kobject_uevent_env(&ts->input_dev->dev.kobj,
- KOBJ_ONLINE, envp);
+ kobject_uevent_env(&ts->input_dev->dev.kobj,
+ KOBJ_ONLINE, envp);
- if (ts->power_sig_enabled) {
- pr_err("%s: power_sig is enabled, wait for signal\n",
+ if (ts->power_sig_enabled) {
+ pr_err("%s: power_sig is enabled, wait for signal\n",
__func__);
- mutex_unlock(&hbtp->mutex);
- rc = wait_for_completion_interruptible(
+ mutex_unlock(&hbtp->mutex);
+ rc = wait_for_completion_interruptible(
&hbtp->power_resume_sig);
- if (rc != 0) {
- pr_err("%s: wait for resume is interrupted\n",
+ if (rc != 0) {
+ pr_err("%s: wait for resume is interrupted\n",
__func__);
- }
- mutex_lock(&hbtp->mutex);
- pr_debug("%s: wait is done\n", __func__);
- } else {
- pr_debug("%s: power_sig is NOT enabled\n",
- __func__);
- }
-
- if (ts->fb_resume_seq_delay) {
- usleep_range(ts->fb_resume_seq_delay,
- ts->fb_resume_seq_delay +
- HBTP_HOLD_DURATION_US);
- pr_err("%s: fb_resume_seq_delay = %u\n",
- __func__, ts->fb_resume_seq_delay);
}
+ mutex_lock(&hbtp->mutex);
+ pr_debug("%s: wait is done\n", __func__);
+ } else {
+ pr_debug("%s: power_sig is NOT enabled\n",
+ __func__);
}
}
+
mutex_unlock(&hbtp->mutex);
return 0;
@@ -1321,20 +1364,41 @@ err_power_on:
return rc;
}
-static int hbtp_fb_resume(struct hbtp_data *ts)
+static int hbtp_fb_revert_resume(struct hbtp_data *ts)
{
char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
+ int rc = 0;
mutex_lock(&hbtp->mutex);
- if (!ts->power_sync_enabled) {
- pr_debug("%s: power_sync is disabled, send uevent\n", __func__);
+
+ pr_debug("%s: hbtp_fb_revert_resume\n", __func__);
+
+ if (ts->pdev && (!ts->power_sync_enabled)) {
+ pr_debug("%s: power_sync is not enabled\n", __func__);
+
if (ts->input_dev) {
kobject_uevent_env(&ts->input_dev->dev.kobj,
- KOBJ_ONLINE, envp);
+ KOBJ_ONLINE, envp);
+
+ if (ts->power_sig_enabled) {
+ pr_debug("%s: power_sig is enabled, wait for signal\n",
+ __func__);
+ mutex_unlock(&hbtp->mutex);
+ rc = wait_for_completion_interruptible(
+ &hbtp->power_resume_sig);
+ if (rc != 0) {
+ pr_warn("%s: wait for revert resume is interrupted\n",
+ __func__);
+ }
+ pr_debug("%s: wait is done\n", __func__);
+ } else {
+ pr_debug("%s: power_sig is NOT enabled\n",
+ __func__);
+ }
}
}
- mutex_unlock(&hbtp->mutex);
- return 0;
+
+ return rc;
}
static int hbtp_pdev_probe(struct platform_device *pdev)
diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c
index a5ea27ad0e16..fdcc14653b64 100644
--- a/drivers/input/misc/keychord.c
+++ b/drivers/input/misc/keychord.c
@@ -60,6 +60,10 @@ struct keychord_device {
unsigned char head;
unsigned char tail;
__u16 buff[BUFFER_SIZE];
+ /* Bit to serialize writes to this device */
+#define KEYCHORD_BUSY 0x01
+ unsigned long flags;
+ wait_queue_head_t write_waitq;
};
static int check_keychord(struct keychord_device *kdev,
@@ -172,7 +176,6 @@ static int keychord_connect(struct input_handler *handler,
goto err_input_open_device;
pr_info("keychord: using input dev %s for fevent\n", dev->name);
-
return 0;
err_input_open_device:
@@ -225,6 +228,41 @@ static ssize_t keychord_read(struct file *file, char __user *buffer,
}
/*
+ * serializes writes on a device. can use mutex_lock_interruptible()
+ * for this particular use case as well - a matter of preference.
+ */
+static int
+keychord_write_lock(struct keychord_device *kdev)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ while (kdev->flags & KEYCHORD_BUSY) {
+ spin_unlock_irqrestore(&kdev->lock, flags);
+ ret = wait_event_interruptible(kdev->write_waitq,
+ ((kdev->flags & KEYCHORD_BUSY) == 0));
+ if (ret)
+ return ret;
+ spin_lock_irqsave(&kdev->lock, flags);
+ }
+ kdev->flags |= KEYCHORD_BUSY;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+ return 0;
+}
+
+static void
+keychord_write_unlock(struct keychord_device *kdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ kdev->flags &= ~KEYCHORD_BUSY;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+ wake_up_interruptible(&kdev->write_waitq);
+}
+
+/*
* keychord_write is used to configure the driver
*/
static ssize_t keychord_write(struct file *file, const char __user *buffer,
@@ -232,9 +270,11 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer,
{
struct keychord_device *kdev = file->private_data;
struct input_keychord *keychords = 0;
- struct input_keychord *keychord, *next, *end;
+ struct input_keychord *keychord;
int ret, i, key;
unsigned long flags;
+ size_t resid = count;
+ size_t key_bytes;
if (count < sizeof(struct input_keychord))
return -EINVAL;
@@ -248,6 +288,22 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer,
return -EFAULT;
}
+ /*
+ * Serialize writes to this device to prevent various races.
+ * 1) writers racing here could do duplicate input_unregister_handler()
+ * calls, resulting in attempting to unlink a node from a list that
+ * does not exist.
+ * 2) writers racing here could do duplicate input_register_handler() calls
+ * below, resulting in a duplicate insertion of a node into the list.
+ * 3) a double kfree of keychords can occur (in the event that
+ * input_register_handler() fails below.
+ */
+ ret = keychord_write_lock(kdev);
+ if (ret) {
+ kfree(keychords);
+ return ret;
+ }
+
/* unregister handler before changing configuration */
if (kdev->registered) {
input_unregister_handler(&kdev->input_handler);
@@ -265,15 +321,29 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer,
kdev->head = kdev->tail = 0;
keychord = keychords;
- end = (struct input_keychord *)((char *)keychord + count);
- while (keychord < end) {
- next = NEXT_KEYCHORD(keychord);
- if (keychord->count <= 0 || next > end) {
+ while (resid > 0) {
+ /* Is the entire keychord entry header present ? */
+ if (resid < sizeof(struct input_keychord)) {
+ pr_err("keychord: Insufficient bytes present for header %zu\n",
+ resid);
+ goto err_unlock_return;
+ }
+ resid -= sizeof(struct input_keychord);
+ if (keychord->count <= 0) {
pr_err("keychord: invalid keycode count %d\n",
keychord->count);
goto err_unlock_return;
}
+ key_bytes = keychord->count * sizeof(keychord->keycodes[0]);
+ /* Do we have all the expected keycodes ? */
+ if (resid < key_bytes) {
+ pr_err("keychord: Insufficient bytes present for keycount %zu\n",
+ resid);
+ goto err_unlock_return;
+ }
+ resid -= key_bytes;
+
if (keychord->version != KEYCHORD_VERSION) {
pr_err("keychord: unsupported version %d\n",
keychord->version);
@@ -292,7 +362,7 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer,
}
kdev->keychord_count++;
- keychord = next;
+ keychord = NEXT_KEYCHORD(keychord);
}
kdev->keychords = keychords;
@@ -302,15 +372,19 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer,
if (ret) {
kfree(keychords);
kdev->keychords = 0;
+ keychord_write_unlock(kdev);
return ret;
}
kdev->registered = 1;
+ keychord_write_unlock(kdev);
+
return count;
err_unlock_return:
spin_unlock_irqrestore(&kdev->lock, flags);
kfree(keychords);
+ keychord_write_unlock(kdev);
return -EINVAL;
}
@@ -336,6 +410,7 @@ static int keychord_open(struct inode *inode, struct file *file)
spin_lock_init(&kdev->lock);
init_waitqueue_head(&kdev->waitq);
+ init_waitqueue_head(&kdev->write_waitq);
kdev->input_handler.event = keychord_event;
kdev->input_handler.connect = keychord_connect;
@@ -357,6 +432,7 @@ static int keychord_release(struct inode *inode, struct file *file)
if (kdev->registered)
input_unregister_handler(&kdev->input_handler);
+ kfree(kdev->keychords);
kfree(kdev);
return 0;
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index da5458dfb1e3..681dce15fbc8 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -1234,7 +1234,12 @@ static const struct acpi_device_id elan_acpi_id[] = {
{ "ELAN0000", 0 },
{ "ELAN0100", 0 },
{ "ELAN0600", 0 },
+ { "ELAN0602", 0 },
{ "ELAN0605", 0 },
+ { "ELAN0608", 0 },
+ { "ELAN0605", 0 },
+ { "ELAN0609", 0 },
+ { "ELAN060B", 0 },
{ "ELAN1000", 0 },
{ }
};
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index 354d47ecd66a..ce6ff9b301bb 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -265,7 +265,8 @@ static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *fir
if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID)))
return -1;
- if (param[0] != TP_MAGIC_IDENT)
+ /* add new TP ID. */
+ if (!(param[0] & TP_MAGIC_IDENT))
return -1;
if (firmware_id)
diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h
index 5617ed3a7d7a..88055755f82e 100644
--- a/drivers/input/mouse/trackpoint.h
+++ b/drivers/input/mouse/trackpoint.h
@@ -21,8 +21,9 @@
#define TP_COMMAND 0xE2 /* Commands start with this */
#define TP_READ_ID 0xE1 /* Sent for device identification */
-#define TP_MAGIC_IDENT 0x01 /* Sent after a TP_READ_ID followed */
+#define TP_MAGIC_IDENT 0x03 /* Sent after a TP_READ_ID followed */
/* by the firmware ID */
+ /* Firmware ID includes 0x1, 0x2, 0x3 */
/*
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 89abfdb539ac..c84c685056b9 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -434,8 +434,10 @@ static int i8042_start(struct serio *serio)
{
struct i8042_port *port = serio->port_data;
+ spin_lock_irq(&i8042_lock);
port->exists = true;
- mb();
+ spin_unlock_irq(&i8042_lock);
+
return 0;
}
@@ -448,16 +450,20 @@ static void i8042_stop(struct serio *serio)
{
struct i8042_port *port = serio->port_data;
+ spin_lock_irq(&i8042_lock);
port->exists = false;
+ port->serio = NULL;
+ spin_unlock_irq(&i8042_lock);
/*
+ * We need to make sure that interrupt handler finishes using
+ * our serio port before we return from this function.
* We synchronize with both AUX and KBD IRQs because there is
* a (very unlikely) chance that AUX IRQ is raised for KBD port
* and vice versa.
*/
synchronize_irq(I8042_AUX_IRQ);
synchronize_irq(I8042_KBD_IRQ);
- port->serio = NULL;
}
/*
@@ -574,7 +580,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
spin_unlock_irqrestore(&i8042_lock, flags);
- if (likely(port->exists && !filtered))
+ if (likely(serio && !filtered))
serio_interrupt(serio, data, dfl);
out:
diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c
index 78bdd24af28b..08bfb83a9447 100644
--- a/drivers/input/touchscreen/st/fts.c
+++ b/drivers/input/touchscreen/st/fts.c
@@ -1003,7 +1003,10 @@ static unsigned char *fts_status_event_handler(
case FTS_WATER_MODE_ON:
case FTS_WATER_MODE_OFF:
default:
- logError(1, "%s %s Received unhandled status event = %02X %02X %02X %02X %02X %02X %02X %02X\n", tag, __func__, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]);
+ logError(0,
+ "%s %s Received unhandled status event = %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ tag, __func__, event[0], event[1], event[2],
+ event[3], event[4], event[5], event[6], event[7]);
break;
}
@@ -1755,8 +1758,6 @@ static int fts_fb_state_chg_callback(struct notifier_block *nb, unsigned long va
info->resume_bit = 1;
- fts_system_reset();
-
fts_mode_handler(info, 0);
info->sensor_sleep = false;
@@ -1959,9 +1960,9 @@ static int parse_dt(struct device *dev, struct fts_i2c_platform_data *bdata)
bdata->bus_reg_name = name;
logError(0, "%s bus_reg_name = %s\n", tag, name);
- if (of_property_read_bool(np, "st, reset-gpio")) {
+ if (of_property_read_bool(np, "st,reset-gpio")) {
bdata->reset_gpio = of_get_named_gpio_flags(np,
- "st, reset-gpio", 0, NULL);
+ "st,reset-gpio", 0, NULL);
logError(0, "%s reset_gpio =%d\n", tag, bdata->reset_gpio);
} else {
bdata->reset_gpio = GPIO_NOT_DEFINED;
@@ -2210,7 +2211,13 @@ static int fts_probe(struct i2c_client *client,
}
#endif
- queue_delayed_work(info->fwu_workqueue, &info->fwu_work, msecs_to_jiffies(EXP_FN_WORK_DELAY_MS));
+ /*if wanna auto-update FW when probe,
+ * please don't comment the following code
+ */
+
+ /* queue_delayed_work(info->fwu_workqueue, &info->fwu_work,
+ * msecs_to_jiffies(EXP_FN_WORK_DELAY_MS));
+ */
logError(1, "%s Probe Finished!\n", tag);
return OK;
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index c6f74b149706..b30739de79e7 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1727,7 +1727,8 @@ static void arm_smmu_pgtbl_unlock(struct arm_smmu_domain *smmu_domain,
static int arm_smmu_restore_sec_cfg(struct arm_smmu_device *smmu)
{
- int ret, scm_ret;
+ int ret;
+ u64 scm_ret = 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/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c
index 37199b9b2cfa..831a195cb806 100644
--- a/drivers/irqchip/irq-atmel-aic-common.c
+++ b/drivers/irqchip/irq-atmel-aic-common.c
@@ -148,9 +148,9 @@ void __init aic_common_rtc_irq_fixup(struct device_node *root)
struct device_node *np;
void __iomem *regs;
- np = of_find_compatible_node(root, NULL, "atmel,at91rm9200-rtc");
+ np = of_find_compatible_node(NULL, NULL, "atmel,at91rm9200-rtc");
if (!np)
- np = of_find_compatible_node(root, NULL,
+ np = of_find_compatible_node(NULL, NULL,
"atmel,at91sam9x5-rtc");
if (!np)
@@ -202,7 +202,6 @@ void __init aic_common_irq_fixup(const struct of_device_id *matches)
return;
match = of_match_node(matches, root);
- of_node_put(root);
if (match) {
void (*fixup)(struct device_node *) = match->data;
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 2e0f61a2dc3f..9e96d81bc5cd 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -793,6 +793,9 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
int enabled;
u64 val;
+ if (cpu >= nr_cpu_ids)
+ return -EINVAL;
+
if (gic_irq_in_rdist(d))
return -EINVAL;
diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c
index deb89d63a728..e684be1bb7c0 100644
--- a/drivers/irqchip/irq-keystone.c
+++ b/drivers/irqchip/irq-keystone.c
@@ -19,9 +19,9 @@
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/irqchip.h>
-#include <linux/irqchip/chained_irq.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/mfd/syscon.h>
@@ -39,6 +39,7 @@ struct keystone_irq_device {
struct irq_domain *irqd;
struct regmap *devctrl_regs;
u32 devctrl_offset;
+ raw_spinlock_t wa_lock;
};
static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq)
@@ -83,17 +84,15 @@ static void keystone_irq_ack(struct irq_data *d)
/* nothing to do here */
}
-static void keystone_irq_handler(struct irq_desc *desc)
+static irqreturn_t keystone_irq_handler(int irq, void *keystone_irq)
{
- unsigned int irq = irq_desc_get_irq(desc);
- struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc);
+ struct keystone_irq_device *kirq = keystone_irq;
+ unsigned long wa_lock_flags;
unsigned long pending;
int src, virq;
dev_dbg(kirq->dev, "start irq %d\n", irq);
- chained_irq_enter(irq_desc_get_chip(desc), desc);
-
pending = keystone_irq_readl(kirq);
keystone_irq_writel(kirq, pending);
@@ -111,13 +110,15 @@ static void keystone_irq_handler(struct irq_desc *desc)
if (!virq)
dev_warn(kirq->dev, "sporious irq detected hwirq %d, virq %d\n",
src, virq);
+ raw_spin_lock_irqsave(&kirq->wa_lock, wa_lock_flags);
generic_handle_irq(virq);
+ raw_spin_unlock_irqrestore(&kirq->wa_lock,
+ wa_lock_flags);
}
}
- chained_irq_exit(irq_desc_get_chip(desc), desc);
-
dev_dbg(kirq->dev, "end irq %d\n", irq);
+ return IRQ_HANDLED;
}
static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
@@ -182,9 +183,16 @@ static int keystone_irq_probe(struct platform_device *pdev)
return -ENODEV;
}
+ raw_spin_lock_init(&kirq->wa_lock);
+
platform_set_drvdata(pdev, kirq);
- irq_set_chained_handler_and_data(kirq->irq, keystone_irq_handler, kirq);
+ ret = request_irq(kirq->irq, keystone_irq_handler,
+ 0, dev_name(dev), kirq);
+ if (ret) {
+ irq_domain_remove(kirq->irqd);
+ return ret;
+ }
/* clear all source bits */
keystone_irq_writel(kirq, ~0x0);
@@ -199,6 +207,8 @@ static int keystone_irq_remove(struct platform_device *pdev)
struct keystone_irq_device *kirq = platform_get_drvdata(pdev);
int hwirq;
+ free_irq(kirq->irq, kirq);
+
for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++)
irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq));
diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c
index 17304705f2cf..05fa9f7af53c 100644
--- a/drivers/irqchip/irq-mxs.c
+++ b/drivers/irqchip/irq-mxs.c
@@ -131,12 +131,16 @@ static struct irq_chip mxs_icoll_chip = {
.irq_ack = icoll_ack_irq,
.irq_mask = icoll_mask_irq,
.irq_unmask = icoll_unmask_irq,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SKIP_SET_WAKE,
};
static struct irq_chip asm9260_icoll_chip = {
.irq_ack = icoll_ack_irq,
.irq_mask = asm9260_mask_irq,
.irq_unmask = asm9260_unmask_irq,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SKIP_SET_WAKE,
};
asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
index 9b856e1890d1..e4c43a17b333 100644
--- a/drivers/isdn/i4l/isdn_common.c
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -1379,6 +1379,7 @@ isdn_ioctl(struct file *file, uint cmd, ulong arg)
if (arg) {
if (copy_from_user(bname, argp, sizeof(bname) - 1))
return -EFAULT;
+ bname[sizeof(bname)-1] = 0;
} else
return -EINVAL;
ret = mutex_lock_interruptible(&dev->mtx);
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index aa5dd5668528..dbad5c431bcb 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -2611,10 +2611,9 @@ isdn_net_newslave(char *parm)
char newname[10];
if (p) {
- /* Slave-Name MUST not be empty */
- if (!strlen(p + 1))
+ /* Slave-Name MUST not be empty or overflow 'newname' */
+ if (strscpy(newname, p + 1, sizeof(newname)) <= 0)
return NULL;
- strcpy(newname, p + 1);
*p = 0;
/* Master must already exist */
if (!(n = isdn_net_findif(parm)))
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index 9c1e8adaf4fc..bf3fbd00a091 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -2364,7 +2364,7 @@ static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_s
id);
return NULL;
} else {
- rs = kzalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL);
+ rs = kzalloc(sizeof(struct ippp_ccp_reset_state), GFP_ATOMIC);
if (!rs)
return NULL;
rs->state = CCPResetIdle;
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 564d20079715..15c931bbbf65 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -158,6 +158,11 @@
#define FLASH_LED_DISABLE 0x00
#define FLASH_LED_SAFETY_TMR_DISABLED 0x13
#define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750
+#define FLASH_LED_IRES5P0_MAX_CURR_MA 640
+#define FLASH_LED_IRES7P5_MAX_CURR_MA 960
+#define FLASH_LED_IRES10P0_MAX_CURR_MA 1280
+#define FLASH_LED_IRES12P5_MAX_CURR_MA 1600
+#define MAX_IRES_LEVELS 4
/* notifier call chain for flash-led irqs */
static ATOMIC_NOTIFIER_HEAD(irq_notifier_list);
@@ -196,13 +201,15 @@ struct flash_node_data {
struct pinctrl_state *hw_strobe_state_suspend;
int hw_strobe_gpio;
int ires_ua;
+ int default_ires_ua;
int max_current;
int current_ma;
int prev_current_ma;
u8 duration;
u8 id;
u8 type;
- u8 ires;
+ u8 ires_idx;
+ u8 default_ires_idx;
u8 hdrm_val;
u8 current_reg_val;
u8 strobe_ctrl;
@@ -305,6 +312,11 @@ static int otst3_threshold_table[] = {
125, 119, 113, 107, 149, 143, 137, 131,
};
+static int max_ires_curr_ma_table[MAX_IRES_LEVELS] = {
+ FLASH_LED_IRES12P5_MAX_CURR_MA, FLASH_LED_IRES10P0_MAX_CURR_MA,
+ FLASH_LED_IRES7P5_MAX_CURR_MA, FLASH_LED_IRES5P0_MAX_CURR_MA
+};
+
static int qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
{
int rc;
@@ -935,6 +947,7 @@ static void qpnp_flash_led_aggregate_max_current(struct flash_node_data *fnode)
static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
{
+ int i = 0;
int prgm_current_ma = value;
int min_ma = fnode->ires_ua / 1000;
struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev);
@@ -944,7 +957,22 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
else if (value < min_ma)
prgm_current_ma = min_ma;
+ fnode->ires_idx = fnode->default_ires_idx;
+ fnode->ires_ua = fnode->default_ires_ua;
+
prgm_current_ma = min(prgm_current_ma, fnode->max_current);
+ if (prgm_current_ma > max_ires_curr_ma_table[fnode->ires_idx]) {
+ /* find the matching ires */
+ for (i = MAX_IRES_LEVELS - 1; i >= 0; i--) {
+ if (prgm_current_ma <= max_ires_curr_ma_table[i]) {
+ fnode->ires_idx = i;
+ fnode->ires_ua = FLASH_LED_IRES_MIN_UA +
+ (FLASH_LED_IRES_BASE - fnode->ires_idx) *
+ FLASH_LED_IRES_DIVISOR;
+ break;
+ }
+ }
+ }
fnode->current_ma = prgm_current_ma;
fnode->cdev.brightness = prgm_current_ma;
fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma,
@@ -1062,7 +1090,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
val = 0;
for (i = 0; i < led->num_fnodes; i++)
if (snode->led_mask & BIT(led->fnode[i].id))
- val |= led->fnode[i].ires << (led->fnode[i].id * 2);
+ val |= led->fnode[i].ires_idx << (led->fnode[i].id * 2);
rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_IRES(led->base),
FLASH_LED_CURRENT_MASK, val);
@@ -1434,13 +1462,14 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
return rc;
}
- fnode->ires_ua = FLASH_LED_IRES_DEFAULT_UA;
- fnode->ires = FLASH_LED_IRES_DEFAULT_VAL;
+ fnode->default_ires_ua = fnode->ires_ua = FLASH_LED_IRES_DEFAULT_UA;
+ fnode->default_ires_idx = fnode->ires_idx = FLASH_LED_IRES_DEFAULT_VAL;
rc = of_property_read_u32(node, "qcom,ires-ua", &val);
if (!rc) {
- fnode->ires_ua = val;
- fnode->ires = FLASH_LED_IRES_BASE -
- (val - FLASH_LED_IRES_MIN_UA) / FLASH_LED_IRES_DIVISOR;
+ fnode->default_ires_ua = fnode->ires_ua = val;
+ fnode->default_ires_idx = fnode->ires_idx =
+ FLASH_LED_IRES_BASE - (val - FLASH_LED_IRES_MIN_UA) /
+ FLASH_LED_IRES_DIVISOR;
} else if (rc != -EINVAL) {
pr_err("Unable to read current resolution rc=%d\n", rc);
return rc;
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index 950244f1e4e8..c85b3e42c8c8 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -160,18 +160,19 @@
#define QPNP_WLED_MOD_EN_SHFT 7
#define QPNP_WLED_MOD_EN 1
#define QPNP_WLED_GATE_DRV_MASK 0xFE
-#define QPNP_WLED_SYNC_DLY_MASK 0xF8
+#define QPNP_WLED_SYNC_DLY_MASK GENMASK(2, 0)
#define QPNP_WLED_SYNC_DLY_MIN_US 0
#define QPNP_WLED_SYNC_DLY_MAX_US 1400
#define QPNP_WLED_SYNC_DLY_STEP_US 200
#define QPNP_WLED_DEF_SYNC_DLY_US 400
-#define QPNP_WLED_FS_CURR_MASK 0xF0
+#define QPNP_WLED_FS_CURR_MASK GENMASK(3, 0)
#define QPNP_WLED_FS_CURR_MIN_UA 0
#define QPNP_WLED_FS_CURR_MAX_UA 30000
#define QPNP_WLED_FS_CURR_STEP_UA 2500
-#define QPNP_WLED_CABC_MASK 0x7F
+#define QPNP_WLED_CABC_MASK 0x80
#define QPNP_WLED_CABC_SHIFT 7
#define QPNP_WLED_CURR_SINK_SHIFT 4
+#define QPNP_WLED_CURR_SINK_MASK GENMASK(7, 4)
#define QPNP_WLED_BRIGHT_LSB_MASK 0xFF
#define QPNP_WLED_BRIGHT_MSB_SHIFT 8
#define QPNP_WLED_BRIGHT_MSB_MASK 0x0F
@@ -208,12 +209,14 @@
#define QPNP_WLED_SEC_UNLOCK 0xA5
#define QPNP_WLED_MAX_STRINGS 4
+#define QPNP_PM660_WLED_MAX_STRINGS 3
#define WLED_MAX_LEVEL_4095 4095
#define QPNP_WLED_RAMP_DLY_MS 20
#define QPNP_WLED_TRIGGER_NONE "none"
#define QPNP_WLED_STR_SIZE 20
#define QPNP_WLED_MIN_MSLEEP 20
#define QPNP_WLED_SC_DLY_MS 20
+#define QPNP_WLED_SOFT_START_DLY_US 10000
#define NUM_SUPPORTED_AVDD_VOLTAGES 6
#define QPNP_WLED_DFLT_AVDD_MV 7600
@@ -381,6 +384,8 @@ struct qpnp_wled {
u16 ramp_ms;
u16 ramp_step;
u16 cons_sync_write_delay_us;
+ u16 auto_calibration_ovp_count;
+ u16 max_strings;
u8 strings[QPNP_WLED_MAX_STRINGS];
u8 num_strings;
u8 loop_auto_gm_thresh;
@@ -396,6 +401,9 @@ struct qpnp_wled {
bool en_ext_pfet_sc_pro;
bool prev_state;
bool ovp_irq_disabled;
+ bool auto_calib_enabled;
+ bool auto_calib_done;
+ ktime_t start_ovp_fault_time;
};
/* helper to read a pmic register */
@@ -529,9 +537,14 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
{
int i, rc;
u8 reg;
+ u16 low_limit = WLED_MAX_LEVEL_4095 * 4 / 1000;
+
+ /* WLED's lower limit of operation is 0.4% */
+ if (level > 0 && level < low_limit)
+ level = low_limit;
/* set brightness registers */
- for (i = 0; i < wled->num_strings; i++) {
+ for (i = 0; i < wled->max_strings; i++) {
reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
rc = qpnp_wled_write_reg(wled,
QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
@@ -600,7 +613,8 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
* OVP interrupt disabled when the module is disabled.
*/
if (state) {
- usleep_range(10000, 11000);
+ usleep_range(QPNP_WLED_SOFT_START_DLY_US,
+ QPNP_WLED_SOFT_START_DLY_US + 1000);
rc = qpnp_wled_psm_config(wled, false);
if (rc < 0)
return rc;
@@ -873,32 +887,25 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qpnp_wled *wled = dev_get_drvdata(dev);
- int data, i, rc, temp;
+ int data, i, rc;
u8 reg;
rc = kstrtoint(buf, 10, &data);
if (rc)
return rc;
- for (i = 0; i < wled->num_strings; i++) {
+ for (i = 0; i < wled->max_strings; i++) {
if (data < QPNP_WLED_FS_CURR_MIN_UA)
data = QPNP_WLED_FS_CURR_MIN_UA;
else if (data > QPNP_WLED_FS_CURR_MAX_UA)
data = QPNP_WLED_FS_CURR_MAX_UA;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_FS_CURR_REG(wled->sink_base,
- wled->strings[i]), &reg);
+ reg = data / QPNP_WLED_FS_CURR_STEP_UA;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
+ QPNP_WLED_FS_CURR_MASK, reg);
if (rc < 0)
return rc;
- reg &= QPNP_WLED_FS_CURR_MASK;
- temp = data / QPNP_WLED_FS_CURR_STEP_UA;
- reg |= temp;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_FS_CURR_REG(wled->sink_base,
- wled->strings[i]), reg);
- if (rc)
- return rc;
}
wled->fs_curr_ua = data;
@@ -1090,6 +1097,229 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
return 0;
}
+#define AUTO_CALIB_BRIGHTNESS 16
+static int wled_auto_calibrate(struct qpnp_wled *wled)
+{
+ int rc = 0, i;
+ u8 reg = 0, sink_config = 0, sink_test = 0, sink_valid = 0, int_sts;
+
+ mutex_lock(&wled->lock);
+
+ /* disable OVP IRQ */
+ if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
+ disable_irq_nosync(wled->ovp_irq);
+ wled->ovp_irq_disabled = true;
+ }
+
+ /* read configured sink configuration */
+ rc = qpnp_wled_read_reg(wled,
+ QPNP_WLED_CURR_SINK_REG(wled->sink_base), &sink_config);
+ if (rc < 0) {
+ pr_err("Failed to read SINK configuration rc=%d\n", rc);
+ goto failed_calib;
+ }
+
+ /* disable the module before starting calibration */
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+ QPNP_WLED_MODULE_EN_MASK, 0);
+ if (rc < 0) {
+ pr_err("Failed to disable WLED module rc=%d\n", rc);
+ goto failed_calib;
+ }
+
+ /* set low brightness across all sinks */
+ rc = qpnp_wled_set_level(wled, AUTO_CALIB_BRIGHTNESS);
+ if (rc < 0) {
+ pr_err("Failed to set brightness for calibration rc=%d\n", rc);
+ goto failed_calib;
+ }
+
+ /* disable all sinks */
+ rc = qpnp_wled_write_reg(wled,
+ QPNP_WLED_CURR_SINK_REG(wled->sink_base), 0);
+ if (rc < 0) {
+ pr_err("Failed to disable all sinks rc=%d\n", rc);
+ goto failed_calib;
+ }
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+ QPNP_WLED_MODULE_EN_MASK,
+ QPNP_WLED_MODULE_EN_MASK);
+ if (rc < 0) {
+ pr_err("Failed to enable WLED module rc=%d\n", rc);
+ goto failed_calib;
+ }
+ /*
+ * Delay for the WLED soft-start, check the OVP status
+ * only after soft-start is complete
+ */
+ usleep_range(QPNP_WLED_SOFT_START_DLY_US,
+ QPNP_WLED_SOFT_START_DLY_US + 1000);
+
+ /* iterate through the strings one by one */
+ for (i = 0; i < wled->max_strings; i++) {
+ sink_test = 1 << (QPNP_WLED_CURR_SINK_SHIFT + i);
+
+ /* Enable feedback control */
+ rc = qpnp_wled_write_reg(wled,
+ QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
+ i + 1);
+ if (rc < 0) {
+ pr_err("Failed to enable feedback for SINK %d rc = %d\n",
+ i + 1, rc);
+ goto failed_calib;
+ }
+
+ /* enable the sink */
+ rc = qpnp_wled_write_reg(wled,
+ QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_test);
+ if (rc < 0) {
+ pr_err("Failed to configure SINK %d rc=%d\n",
+ i + 1, rc);
+ goto failed_calib;
+ }
+
+ /* delay for WLED soft-start */
+ usleep_range(QPNP_WLED_SOFT_START_DLY_US,
+ QPNP_WLED_SOFT_START_DLY_US + 1000);
+
+ rc = qpnp_wled_read_reg(wled,
+ QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
+ if (rc < 0) {
+ pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
+ goto failed_calib;
+ }
+
+ if (int_sts & QPNP_WLED_OVP_FAULT_BIT)
+ pr_debug("WLED OVP fault detected with SINK %d\n",
+ i + 1);
+ else
+ sink_valid |= sink_test;
+ }
+
+ if (sink_valid == sink_config) {
+ pr_debug("WLED auto-calibration complete, default sink-config=%x OK!\n",
+ sink_config);
+ } else {
+ pr_warn("Invalid WLED default sink config=%x changing it to=%x\n",
+ sink_config, sink_valid);
+ sink_config = sink_valid;
+ }
+
+ if (!sink_config) {
+ pr_warn("No valid WLED sinks found\n");
+ goto failed_calib;
+ }
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+ QPNP_WLED_MODULE_EN_MASK, 0);
+ if (rc < 0) {
+ pr_err("Failed to disable WLED module rc=%d\n", rc);
+ goto failed_calib;
+ }
+
+ /* write the new sink configuration */
+ rc = qpnp_wled_write_reg(wled,
+ QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_config);
+ if (rc < 0) {
+ pr_err("Failed to reconfigure the default sink rc=%d\n", rc);
+ goto failed_calib;
+ }
+
+ /* MODULATOR_EN setting for valid sinks */
+ for (i = 0; i < wled->max_strings; i++) {
+ if (sink_config & (1 << (QPNP_WLED_CURR_SINK_SHIFT + i)))
+ reg = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
+ else
+ reg = 0x0; /* disable modulator_en for unused sink */
+
+ if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
+ reg &= QPNP_WLED_GATE_DRV_MASK;
+ else
+ reg |= ~QPNP_WLED_GATE_DRV_MASK;
+
+ rc = qpnp_wled_write_reg(wled,
+ QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
+ if (rc < 0) {
+ pr_err("Failed to configure MODULATOR_EN rc=%d\n", rc);
+ goto failed_calib;
+ }
+ }
+
+ /* restore the feedback setting */
+ rc = qpnp_wled_write_reg(wled,
+ QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
+ wled->fdbk_op);
+ if (rc < 0) {
+ pr_err("Failed to restore feedback setting rc=%d\n", rc);
+ goto failed_calib;
+ }
+
+ /* restore brightness */
+ rc = qpnp_wled_set_level(wled, wled->cdev.brightness);
+ if (rc < 0) {
+ pr_err("Failed to set brightness after calibration rc=%d\n",
+ rc);
+ goto failed_calib;
+ }
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+ QPNP_WLED_MODULE_EN_MASK,
+ QPNP_WLED_MODULE_EN_MASK);
+ if (rc < 0) {
+ pr_err("Failed to enable WLED module rc=%d\n", rc);
+ goto failed_calib;
+ }
+
+ /* delay for WLED soft-start */
+ usleep_range(QPNP_WLED_SOFT_START_DLY_US,
+ QPNP_WLED_SOFT_START_DLY_US + 1000);
+
+failed_calib:
+ if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
+ enable_irq(wled->ovp_irq);
+ wled->ovp_irq_disabled = false;
+ }
+ mutex_unlock(&wled->lock);
+ return rc;
+}
+
+#define WLED_AUTO_CAL_OVP_COUNT 5
+#define WLED_AUTO_CAL_CNT_DLY_US 1000000 /* 1 second */
+static bool qpnp_wled_auto_cal_required(struct qpnp_wled *wled)
+{
+ s64 elapsed_time_us;
+
+ /*
+ * Check if the OVP fault was an occasional one
+ * or if its firing continuously, the latter qualifies
+ * for an auto-calibration check.
+ */
+ if (!wled->auto_calibration_ovp_count) {
+ wled->start_ovp_fault_time = ktime_get();
+ wled->auto_calibration_ovp_count++;
+ } else {
+ elapsed_time_us = ktime_us_delta(ktime_get(),
+ wled->start_ovp_fault_time);
+ if (elapsed_time_us > WLED_AUTO_CAL_CNT_DLY_US)
+ wled->auto_calibration_ovp_count = 0;
+ else
+ wled->auto_calibration_ovp_count++;
+
+ if (wled->auto_calibration_ovp_count >=
+ WLED_AUTO_CAL_OVP_COUNT) {
+ wled->auto_calibration_ovp_count = 0;
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* ovp irq handler */
static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
{
@@ -1114,6 +1344,21 @@ static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
if (fault_sts & (QPNP_WLED_OVP_FAULT_BIT | QPNP_WLED_ILIM_FAULT_BIT))
pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
int_sts, fault_sts);
+
+ if (fault_sts & QPNP_WLED_OVP_FAULT_BIT) {
+ if (wled->auto_calib_enabled && !wled->auto_calib_done) {
+ if (qpnp_wled_auto_cal_required(wled)) {
+ rc = wled_auto_calibrate(wled);
+ if (rc < 0) {
+ pr_err("Failed auto-calibration rc=%d\n",
+ rc);
+ return IRQ_HANDLED;
+ }
+ wled->auto_calib_done = true;
+ }
+ }
+ }
+
return IRQ_HANDLED;
}
@@ -1423,7 +1668,7 @@ static int qpnp_wled_vref_config(struct qpnp_wled *wled)
static int qpnp_wled_config(struct qpnp_wled *wled)
{
int rc, i, temp;
- u8 reg = 0;
+ u8 reg = 0, sink_en = 0, mask;
/* Configure display type */
rc = qpnp_wled_set_disp(wled, wled->ctrl_base);
@@ -1622,93 +1867,77 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
rc = qpnp_wled_write_reg(wled, QPNP_WLED_CURR_SINK_REG(wled->sink_base),
reg);
- for (i = 0; i < wled->num_strings; i++) {
- if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) {
- dev_err(&wled->pdev->dev, "Invalid string number\n");
- return -EINVAL;
- }
-
- /* MODULATOR */
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_MOD_EN_REG(wled->sink_base,
- wled->strings[i]), &reg);
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_MOD_EN_MASK;
- reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
-
- if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
- reg &= QPNP_WLED_GATE_DRV_MASK;
- else
- reg |= ~QPNP_WLED_GATE_DRV_MASK;
-
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_MOD_EN_REG(wled->sink_base,
- wled->strings[i]), reg);
- if (rc)
- return rc;
-
+ for (i = 0; i < wled->max_strings; i++) {
/* SYNC DELAY */
if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
- wled->strings[i]), &reg);
+ reg = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
+ mask = QPNP_WLED_SYNC_DLY_MASK;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_SYNC_DLY_REG(wled->sink_base, i),
+ mask, reg);
if (rc < 0)
return rc;
- reg &= QPNP_WLED_SYNC_DLY_MASK;
- temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
- reg |= temp;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
- wled->strings[i]), reg);
- if (rc)
- return rc;
/* FULL SCALE CURRENT */
if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_FS_CURR_REG(wled->sink_base,
- wled->strings[i]), &reg);
+ reg = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
+ mask = QPNP_WLED_FS_CURR_MASK;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
+ mask, reg);
if (rc < 0)
return rc;
- reg &= QPNP_WLED_FS_CURR_MASK;
- temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
- reg |= temp;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_FS_CURR_REG(wled->sink_base,
- wled->strings[i]), reg);
- if (rc)
- return rc;
/* CABC */
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_CABC_REG(wled->sink_base,
- wled->strings[i]), &reg);
+ reg = wled->en_cabc ? (1 << QPNP_WLED_CABC_SHIFT) : 0;
+ mask = QPNP_WLED_CABC_MASK;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_CABC_REG(wled->sink_base, i),
+ mask, reg);
if (rc < 0)
return rc;
- reg &= QPNP_WLED_CABC_MASK;
- reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT);
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_CABC_REG(wled->sink_base,
- wled->strings[i]), reg);
- if (rc)
- return rc;
+ }
- /* Enable CURRENT SINK */
+ /* Settings specific to valid sinks */
+ for (i = 0; i < wled->num_strings; i++) {
+ if (wled->strings[i] >= wled->max_strings) {
+ dev_err(&wled->pdev->dev, "Invalid string number\n");
+ return -EINVAL;
+ }
+ /* MODULATOR */
rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_CURR_SINK_REG(wled->sink_base), &reg);
+ QPNP_WLED_MOD_EN_REG(wled->sink_base, i), &reg);
if (rc < 0)
return rc;
- temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
- reg |= (1 << temp);
+ reg &= QPNP_WLED_MOD_EN_MASK;
+ reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
+
+ if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
+ reg &= QPNP_WLED_GATE_DRV_MASK;
+ else
+ reg |= ~QPNP_WLED_GATE_DRV_MASK;
+
rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_CURR_SINK_REG(wled->sink_base), reg);
+ QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
if (rc)
return rc;
+
+ /* SINK EN */
+ temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
+ sink_en |= (1 << temp);
+ }
+ mask = QPNP_WLED_CURR_SINK_MASK;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_CURR_SINK_REG(wled->sink_base),
+ mask, sink_en);
+ if (rc < 0) {
+ dev_err(&wled->pdev->dev,
+ "Failed to enable WLED sink config rc = %d\n", rc);
+ return rc;
}
rc = qpnp_wled_sync_reg_toggle(wled);
@@ -1728,8 +1957,13 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
wled->ovp_irq, rc);
return rc;
}
- disable_irq(wled->ovp_irq);
- wled->ovp_irq_disabled = true;
+ rc = qpnp_wled_read_reg(wled,
+ QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), &reg);
+ /* disable the OVP irq only if the module is not enabled */
+ if (!rc && !(reg & QPNP_WLED_MODULE_EN_MASK)) {
+ disable_irq(wled->ovp_irq);
+ wled->ovp_irq_disabled = true;
+ }
}
if (wled->sc_irq >= 0) {
@@ -2091,11 +2325,16 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
wled->en_cabc = of_property_read_bool(pdev->dev.of_node,
"qcom,en-cabc");
+ if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+ wled->max_strings = QPNP_PM660_WLED_MAX_STRINGS;
+ else
+ wled->max_strings = QPNP_WLED_MAX_STRINGS;
+
prop = of_find_property(pdev->dev.of_node,
"qcom,led-strings-list", &temp_val);
if (!prop || !temp_val || temp_val > QPNP_WLED_MAX_STRINGS) {
dev_err(&pdev->dev, "Invalid strings info, use default");
- wled->num_strings = QPNP_WLED_MAX_STRINGS;
+ wled->num_strings = wled->max_strings;
for (i = 0; i < wled->num_strings; i++)
wled->strings[i] = i;
} else {
@@ -2118,6 +2357,9 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
wled->lcd_psm_ctrl = of_property_read_bool(pdev->dev.of_node,
"qcom,lcd-psm-ctrl");
+
+ wled->auto_calib_enabled = of_property_read_bool(pdev->dev.of_node,
+ "qcom,auto-calibration-enable");
return 0;
}
@@ -2185,13 +2427,13 @@ static int qpnp_wled_probe(struct platform_device *pdev)
}
mutex_init(&wled->bus_lock);
+ mutex_init(&wled->lock);
rc = qpnp_wled_config(wled);
if (rc) {
dev_err(&pdev->dev, "wled config failed\n");
return rc;
}
- mutex_init(&wled->lock);
INIT_WORK(&wled->work, qpnp_wled_work);
wled->ramp_ms = QPNP_WLED_RAMP_DLY_MS;
wled->ramp_step = 1;
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index ab0e4f99ebb9..e3cfffb5c563 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -875,6 +875,14 @@ static int qpnp_mpp_set(struct qpnp_led_data *led)
}
if (led->mpp_cfg->pwm_mode == PWM_MODE) {
/*config pwm for brightness scaling*/
+ rc = pwm_change_mode(led->mpp_cfg->pwm_cfg->pwm_dev,
+ PM_PWM_MODE_PWM);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Failed to set PWM mode, rc = %d\n",
+ rc);
+ return rc;
+ }
period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
if (period_us > INT_MAX / NSEC_PER_USEC) {
duty_us = (period_us * led->cdev.brightness) /
@@ -1581,6 +1589,14 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
}
if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
+ rc = pwm_change_mode(led->kpdbl_cfg->pwm_cfg->pwm_dev,
+ PM_PWM_MODE_PWM);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Failed to set PWM mode, rc = %d\n",
+ rc);
+ return rc;
+ }
period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
if (period_us > INT_MAX / NSEC_PER_USEC) {
duty_us = (period_us * led->cdev.brightness) /
@@ -1702,6 +1718,14 @@ static int qpnp_rgb_set(struct qpnp_led_data *led)
led->rgb_cfg->pwm_cfg->mode =
led->rgb_cfg->pwm_cfg->default_mode;
if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
+ rc = pwm_change_mode(led->rgb_cfg->pwm_cfg->pwm_dev,
+ PM_PWM_MODE_PWM);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Failed to set PWM mode, rc = %d\n",
+ rc);
+ return rc;
+ }
period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
if (period_us > INT_MAX / NSEC_PER_USEC) {
duty_us = (period_us * led->cdev.brightness) /
@@ -2136,6 +2160,11 @@ static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg,
dev_err(&pdev->dev, "Failed to configure pwm LUT\n");
return rc;
}
+ rc = pwm_change_mode(pwm_cfg->pwm_dev, PM_PWM_MODE_LPG);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Failed to set LPG mode\n");
+ return rc;
+ }
}
} else {
dev_err(&pdev->dev, "Invalid PWM device\n");
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index 6a4811f85705..9cf826df89b1 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -104,11 +104,14 @@ static void tx_tick(struct mbox_chan *chan, int r)
/* Submit next message */
msg_submit(chan);
+ if (!mssg)
+ return;
+
/* Notify the client */
- if (mssg && chan->cl->tx_done)
+ if (chan->cl->tx_done)
chan->cl->tx_done(chan->cl, mssg, r);
- if (chan->cl->tx_block)
+ if (r != -ETIME && chan->cl->tx_block)
complete(&chan->tx_complete);
}
@@ -261,7 +264,7 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
msg_submit(chan);
- if (chan->cl->tx_block && chan->active_req) {
+ if (chan->cl->tx_block) {
unsigned long wait;
int ret;
@@ -272,8 +275,8 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
ret = wait_for_completion_timeout(&chan->tx_complete, wait);
if (ret == 0) {
- t = -EIO;
- tx_tick(chan, -EIO);
+ t = -ETIME;
+ tx_tick(chan, t);
}
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index eff554a12fb4..0a856cb181e9 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1866,7 +1866,7 @@ super_1_rdev_size_change(struct md_rdev *rdev, sector_t num_sectors)
}
sb = page_address(rdev->sb_page);
sb->data_size = cpu_to_le64(num_sectors);
- sb->super_offset = rdev->sb_start;
+ sb->super_offset = cpu_to_le64(rdev->sb_start);
sb->sb_csum = calc_sb_1_csum(sb);
md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
rdev->sb_page);
@@ -2273,7 +2273,7 @@ static bool does_sb_need_changing(struct mddev *mddev)
/* Check if any mddev parameters have changed */
if ((mddev->dev_sectors != le64_to_cpu(sb->size)) ||
(mddev->reshape_position != le64_to_cpu(sb->reshape_position)) ||
- (mddev->layout != le64_to_cpu(sb->layout)) ||
+ (mddev->layout != le32_to_cpu(sb->layout)) ||
(mddev->raid_disks != le32_to_cpu(sb->raid_disks)) ||
(mddev->chunk_sectors != le32_to_cpu(sb->chunksize)))
return true;
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index d81be5e471d0..f24a9e14021d 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1088,7 +1088,7 @@ static void make_request(struct mddev *mddev, struct bio * bio)
*/
DEFINE_WAIT(w);
for (;;) {
- flush_signals(current);
+ sigset_t full, old;
prepare_to_wait(&conf->wait_barrier,
&w, TASK_INTERRUPTIBLE);
if (bio_end_sector(bio) <= mddev->suspend_lo ||
@@ -1097,7 +1097,10 @@ static void make_request(struct mddev *mddev, struct bio * bio)
!md_cluster_ops->area_resyncing(mddev, WRITE,
bio->bi_iter.bi_sector, bio_end_sector(bio))))
break;
+ sigfillset(&full);
+ sigprocmask(SIG_BLOCK, &full, &old);
schedule();
+ sigprocmask(SIG_SETMASK, &old, NULL);
}
finish_wait(&conf->wait_barrier, &w);
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 4384b46cee1a..8f60520c8392 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5279,12 +5279,15 @@ static void make_request(struct mddev *mddev, struct bio * bi)
* userspace, we want an interruptible
* wait.
*/
- flush_signals(current);
prepare_to_wait(&conf->wait_for_overlap,
&w, TASK_INTERRUPTIBLE);
if (logical_sector >= mddev->suspend_lo &&
logical_sector < mddev->suspend_hi) {
+ sigset_t full, old;
+ sigfillset(&full);
+ sigprocmask(SIG_BLOCK, &full, &old);
schedule();
+ sigprocmask(SIG_SETMASK, &old, NULL);
do_prepare = true;
}
goto retry;
@@ -5818,6 +5821,8 @@ static void raid5_do_work(struct work_struct *work)
pr_debug("%d stripes handled\n", handled);
spin_unlock_irq(&conf->device_lock);
+
+ async_tx_issue_pending_all();
blk_finish_plug(&plug);
pr_debug("--- raid5worker inactive\n");
@@ -7528,12 +7533,10 @@ static void end_reshape(struct r5conf *conf)
{
if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) {
- struct md_rdev *rdev;
spin_lock_irq(&conf->device_lock);
conf->previous_raid_disks = conf->raid_disks;
- rdev_for_each(rdev, conf->mddev)
- rdev->data_offset = rdev->new_data_offset;
+ md_finish_reshape(conf->mddev);
smp_wmb();
conf->reshape_progress = MaxSector;
conf->mddev->reshape_position = MaxSector;
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
index 8001cde1db1e..503135a4f47a 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
@@ -211,7 +211,7 @@ static int s5c73m3_3a_lock(struct s5c73m3 *state, struct v4l2_ctrl *ctrl)
}
if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS)
- ret = s5c73m3_af_run(state, ~af_lock);
+ ret = s5c73m3_af_run(state, !af_lock);
return ret;
}
diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c
index 8f2556ec3971..61611d1682d1 100644
--- a/drivers/media/pci/cx88/cx88-cards.c
+++ b/drivers/media/pci/cx88/cx88-cards.c
@@ -3691,7 +3691,14 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
core->nr = nr;
sprintf(core->name, "cx88[%d]", core->nr);
- core->tvnorm = V4L2_STD_NTSC_M;
+ /*
+ * Note: Setting initial standard here would cause first call to
+ * cx88_set_tvnorm() to return without programming any registers. Leave
+ * it blank for at this point and it will get set later in
+ * cx8800_initdev()
+ */
+ core->tvnorm = 0;
+
core->width = 320;
core->height = 240;
core->field = V4L2_FIELD_INTERLACED;
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index aef9acf351f6..abbf5b05b6f5 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -1429,7 +1429,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
/* initial device configuration */
mutex_lock(&core->lock);
- cx88_set_tvnorm(core, core->tvnorm);
+ cx88_set_tvnorm(core, V4L2_STD_NTSC_M);
v4l2_ctrl_handler_setup(&core->video_hdl);
v4l2_ctrl_handler_setup(&core->audio_hdl);
cx88_video_mux(core, 0);
diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c
index 8ef6399d794f..bc957528f69f 100644
--- a/drivers/media/pci/saa7134/saa7134-i2c.c
+++ b/drivers/media/pci/saa7134/saa7134-i2c.c
@@ -355,12 +355,43 @@ static struct i2c_client saa7134_client_template = {
/* ----------------------------------------------------------- */
+/* On Medion 7134 reading EEPROM needs DVB-T demod i2c gate open */
+static void saa7134_i2c_eeprom_md7134_gate(struct saa7134_dev *dev)
+{
+ u8 subaddr = 0x7, dmdregval;
+ u8 data[2];
+ int ret;
+ struct i2c_msg i2cgatemsg_r[] = { {.addr = 0x08, .flags = 0,
+ .buf = &subaddr, .len = 1},
+ {.addr = 0x08,
+ .flags = I2C_M_RD,
+ .buf = &dmdregval, .len = 1}
+ };
+ struct i2c_msg i2cgatemsg_w[] = { {.addr = 0x08, .flags = 0,
+ .buf = data, .len = 2} };
+
+ ret = i2c_transfer(&dev->i2c_adap, i2cgatemsg_r, 2);
+ if ((ret == 2) && (dmdregval & 0x2)) {
+ pr_debug("%s: DVB-T demod i2c gate was left closed\n",
+ dev->name);
+
+ data[0] = subaddr;
+ data[1] = (dmdregval & ~0x2);
+ if (i2c_transfer(&dev->i2c_adap, i2cgatemsg_w, 1) != 1)
+ pr_err("%s: EEPROM i2c gate open failure\n",
+ dev->name);
+ }
+}
+
static int
saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len)
{
unsigned char buf;
int i,err;
+ if (dev->board == SAA7134_BOARD_MD7134)
+ saa7134_i2c_eeprom_md7134_gate(dev);
+
dev->i2c_client.addr = 0xa0 >> 1;
buf = 0;
if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) {
diff --git a/drivers/media/pci/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c
index a18fe5d47238..b4857cd7069e 100644
--- a/drivers/media/pci/saa7164/saa7164-bus.c
+++ b/drivers/media/pci/saa7164/saa7164-bus.c
@@ -393,11 +393,11 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
msg_tmp.size = le16_to_cpu((__force __le16)msg_tmp.size);
msg_tmp.command = le32_to_cpu((__force __le32)msg_tmp.command);
msg_tmp.controlselector = le16_to_cpu((__force __le16)msg_tmp.controlselector);
+ memcpy(msg, &msg_tmp, sizeof(*msg));
/* No need to update the read positions, because this was a peek */
/* If the caller specifically want to peek, return */
if (peekonly) {
- memcpy(msg, &msg_tmp, sizeof(*msg));
goto peekout;
}
@@ -442,21 +442,15 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
space_rem = bus->m_dwSizeGetRing - curr_grp;
if (space_rem < sizeof(*msg)) {
- /* msg wraps around the ring */
- memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, space_rem);
- memcpy_fromio((u8 *)msg + space_rem, bus->m_pdwGetRing,
- sizeof(*msg) - space_rem);
if (buf)
memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) -
space_rem, buf_size);
} else if (space_rem == sizeof(*msg)) {
- memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
if (buf)
memcpy_fromio(buf, bus->m_pdwGetRing, buf_size);
} else {
/* Additional data wraps around the ring */
- memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
if (buf) {
memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp +
sizeof(*msg), space_rem - sizeof(*msg));
@@ -469,15 +463,10 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
} else {
/* No wrapping */
- memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
if (buf)
memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
buf_size);
}
- /* Convert from little endian to CPU */
- msg->size = le16_to_cpu((__force __le16)msg->size);
- msg->command = le32_to_cpu((__force __le32)msg->command);
- msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector);
/* Update the read positions, adjusting the ring */
saa7164_writel(bus->m_dwGetReadPos, new_grp);
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index 7767e072d623..1f656a3a84b9 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -1709,27 +1709,9 @@ static long vpfe_param_handler(struct file *file, void *priv,
switch (cmd) {
case VPFE_CMD_S_CCDC_RAW_PARAMS:
+ ret = -EINVAL;
v4l2_warn(&vpfe_dev->v4l2_dev,
- "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n");
- if (ccdc_dev->hw_ops.set_params) {
- ret = ccdc_dev->hw_ops.set_params(param);
- if (ret) {
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "Error setting parameters in CCDC\n");
- goto unlock_out;
- }
- ret = vpfe_get_ccdc_image_format(vpfe_dev,
- &vpfe_dev->fmt);
- if (ret < 0) {
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "Invalid image format at CCDC\n");
- goto unlock_out;
- }
- } else {
- ret = -EINVAL;
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n");
- }
+ "VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n");
break;
default:
ret = -ENOTTY;
diff --git a/drivers/media/platform/msm/ais/fd/msm_fd_dev.c b/drivers/media/platform/msm/ais/fd/msm_fd_dev.c
index 420083f019cf..d9e109938e7e 100644
--- a/drivers/media/platform/msm/ais/fd/msm_fd_dev.c
+++ b/drivers/media/platform/msm/ais/fd/msm_fd_dev.c
@@ -430,6 +430,7 @@ static int msm_fd_open(struct file *file)
ctx->vb2_q.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
ctx->vb2_q.io_modes = VB2_USERPTR;
ctx->vb2_q.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ mutex_init(&ctx->lock);
ret = vb2_queue_init(&ctx->vb2_q);
if (ret < 0) {
dev_err(device->dev, "Error queue init\n");
@@ -480,7 +481,9 @@ static int msm_fd_release(struct file *file)
msm_cpp_vbif_register_error_handler((void *)ctx,
VBIF_CLIENT_FD, NULL);
+ mutex_lock(&ctx->lock);
vb2_queue_release(&ctx->vb2_q);
+ mutex_unlock(&ctx->lock);
vfree(ctx->stats);
@@ -510,7 +513,9 @@ static unsigned int msm_fd_poll(struct file *file,
struct fd_ctx *ctx = msm_fd_ctx_from_fh(file->private_data);
unsigned int ret;
+ mutex_lock(&ctx->lock);
ret = vb2_poll(&ctx->vb2_q, file, wait);
+ mutex_unlock(&ctx->lock);
if (atomic_read(&ctx->subscribed_for_event)) {
poll_wait(file, &ctx->fh.wait, wait);
@@ -748,9 +753,9 @@ static int msm_fd_reqbufs(struct file *file,
int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- mutex_lock(&ctx->fd_device->recovery_lock);
+ mutex_lock(&ctx->lock);
ret = vb2_reqbufs(&ctx->vb2_q, req);
- mutex_unlock(&ctx->fd_device->recovery_lock);
+ mutex_unlock(&ctx->lock);
return ret;
}
@@ -766,9 +771,9 @@ static int msm_fd_qbuf(struct file *file, void *fh,
int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- mutex_lock(&ctx->fd_device->recovery_lock);
+ mutex_lock(&ctx->lock);
ret = vb2_qbuf(&ctx->vb2_q, pb);
- mutex_unlock(&ctx->fd_device->recovery_lock);
+ mutex_unlock(&ctx->lock);
return ret;
}
@@ -785,9 +790,9 @@ static int msm_fd_dqbuf(struct file *file,
int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- mutex_lock(&ctx->fd_device->recovery_lock);
+ mutex_lock(&ctx->lock);
ret = vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK);
- mutex_unlock(&ctx->fd_device->recovery_lock);
+ mutex_unlock(&ctx->lock);
return ret;
}
@@ -803,7 +808,9 @@ static int msm_fd_streamon(struct file *file,
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
int ret;
+ mutex_lock(&ctx->lock);
ret = vb2_streamon(&ctx->vb2_q, buf_type);
+ mutex_unlock(&ctx->lock);
if (ret < 0)
dev_err(ctx->fd_device->dev, "Stream on fails\n");
@@ -822,7 +829,9 @@ static int msm_fd_streamoff(struct file *file,
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
int ret;
+ mutex_lock(&ctx->lock);
ret = vb2_streamoff(&ctx->vb2_q, buf_type);
+ mutex_unlock(&ctx->lock);
if (ret < 0)
dev_err(ctx->fd_device->dev, "Stream off fails\n");
@@ -1053,14 +1062,18 @@ static int msm_fd_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
a->value = ctx->format.size->work_size;
break;
case V4L2_CID_FD_WORK_MEMORY_FD:
+ mutex_lock(&ctx->fd_device->recovery_lock);
if (ctx->work_buf.fd != -1)
msm_fd_hw_unmap_buffer(&ctx->work_buf);
if (a->value >= 0) {
ret = msm_fd_hw_map_buffer(&ctx->mem_pool,
a->value, &ctx->work_buf);
- if (ret < 0)
+ if (ret < 0) {
+ mutex_unlock(&ctx->fd_device->recovery_lock);
return ret;
+ }
}
+ mutex_unlock(&ctx->fd_device->recovery_lock);
break;
default:
return -EINVAL;
diff --git a/drivers/media/platform/msm/ais/fd/msm_fd_dev.h b/drivers/media/platform/msm/ais/fd/msm_fd_dev.h
index c15032256f4d..a7615a65d2fc 100644
--- a/drivers/media/platform/msm/ais/fd/msm_fd_dev.h
+++ b/drivers/media/platform/msm/ais/fd/msm_fd_dev.h
@@ -161,6 +161,7 @@ struct fd_ctx {
struct msm_fd_mem_pool mem_pool;
struct msm_fd_stats *stats;
struct msm_fd_buf_handle work_buf;
+ struct mutex lock;
};
/*
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp.h b/drivers/media/platform/msm/ais/isp/msm_isp.h
index 72a76d178aa8..86974eeb4a32 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp.h
+++ b/drivers/media/platform/msm/ais/isp/msm_isp.h
@@ -355,6 +355,7 @@ struct msm_vfe_hardware_info {
uint32_t dmi_reg_offset;
uint32_t min_ab;
uint32_t min_ib;
+ uint32_t regulator_num;
const char *regulator_names[];
};
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp47.c b/drivers/media/platform/msm/ais/isp/msm_isp47.c
index d63282f80aca..04e879fc3bcf 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp47.c
@@ -699,6 +699,12 @@ void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
vfe_dev->reg_update_requested;
if ((vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE1) &&
((frame_src == VFE_PIX_0) || (frame_src == VFE_SRC_MAX))) {
+ if (!vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]) {
+ pr_err("%s vfe_base for ISP_VFE0 is NULL\n", __func__);
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock,
+ flags);
+ return;
+ }
msm_camera_io_w_mb(update_mask,
vfe_dev->common_data->dual_vfe_res->
vfe_base[ISP_VFE0] + 0x4AC);
@@ -2537,8 +2543,7 @@ int msm_vfe47_get_regulators(struct vfe_device *vfe_dev)
int rc = 0;
int i;
- vfe_dev->vfe_num_regulators =
- sizeof(*vfe_dev->hw_info->regulator_names) / sizeof(char *);
+ vfe_dev->vfe_num_regulators = vfe_dev->hw_info->regulator_num;
vfe_dev->regulator_info = kzalloc(sizeof(struct msm_cam_regulator) *
vfe_dev->vfe_num_regulators, GFP_KERNEL);
@@ -2811,6 +2816,7 @@ struct msm_vfe_hardware_info vfe47_hw_info = {
.dmi_reg_offset = 0xC2C,
.axi_hw_info = &msm_vfe47_axi_hw_info,
.stats_hw_info = &msm_vfe47_stats_hw_info,
+ .regulator_num = 3,
.regulator_names = {"vdd", "camss-vdd", "mmagic-vdd"},
};
EXPORT_SYMBOL(vfe47_hw_info);
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c
index 6e89544161ee..0d08cffda25c 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c
@@ -891,6 +891,12 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL;
struct msm_isp_sw_framskip *sw_skip_info = NULL;
+ if (update_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s: Invalid num_streams %d\n",
+ __func__, update_cmd->num_streams);
+ return -EINVAL;
+ }
+
/* validate request */
for (i = 0; i < update_cmd->num_streams; i++) {
update_info = (struct msm_vfe_axi_stream_cfg_update_info *)
diff --git a/drivers/media/platform/msm/ais/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/cci/Makefile b/drivers/media/platform/msm/ais/sensor/cci/Makefile
index 3942508c0d66..b8b8c83bc6de 100644
--- a/drivers/media/platform/msm/ais/sensor/cci/Makefile
+++ b/drivers/media/platform/msm/ais/sensor/cci/Makefile
@@ -2,3 +2,4 @@ ccflags-y += -Idrivers/media/platform/msm/ais
ccflags-y += -Idrivers/media/platform/msm/ais/common
ccflags-y += -Idrivers/media/platform/msm/ais/sensor/io
obj-$(CONFIG_MSM_AIS) += msm_cci.o
+obj-$(CONFIG_MSM_AIS) += msm_early_cam.o
diff --git a/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.c b/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.c
new file mode 100644
index 000000000000..00ec613e7303
--- /dev/null
+++ b/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.c
@@ -0,0 +1,279 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include "msm_sd.h"
+#include "msm_early_cam.h"
+#include "msm_cam_cci_hwreg.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_dt_util.h"
+#include "cam_hw_ops.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#undef EARLY_CAM_DBG
+#ifdef MSM_EARLY_CAM_DEBUG
+#define EARLY_CAM_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define EARLY_CAM_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#define MSM_EARLY_CAM_DRV_NAME "msm_early_cam"
+static struct platform_driver msm_early_camera_driver;
+static struct early_cam_device *new_early_cam_dev;
+
+int msm_early_cam_disable_clocks(void)
+{
+ int rc = 0;
+
+ CDBG("%s:\n", __func__);
+ /* Vote OFF for clocks */
+ if (new_early_cam_dev == NULL) {
+ rc = -EINVAL;
+ pr_err("%s: clock structure uninitialised %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ if ((new_early_cam_dev->pdev == NULL) ||
+ (new_early_cam_dev->early_cam_clk_info == NULL) ||
+ (new_early_cam_dev->early_cam_clk == NULL) ||
+ (new_early_cam_dev->num_clk == 0)) {
+ rc = -EINVAL;
+ pr_err("%s: Clock details uninitialised %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ rc = msm_camera_clk_enable(&new_early_cam_dev->pdev->dev,
+ new_early_cam_dev->early_cam_clk_info,
+ new_early_cam_dev->early_cam_clk,
+ new_early_cam_dev->num_clk, false);
+ if (rc < 0) {
+ pr_err("%s: clk disable failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
+ CAM_AHB_SUSPEND_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote OFF AHB_CLIENT_CSIPHY %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID,
+ CAM_AHB_SUSPEND_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote OFF AHB_CLIENT_CSID %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI,
+ CAM_AHB_SUSPEND_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote OFF AHB_CLIENT_CCI %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_ISPIF,
+ CAM_AHB_SUSPEND_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote OFF AHB_CLIENT_ISPIF %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_VFE0,
+ CAM_AHB_SUSPEND_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote OFF AHB_CLIENT_VFE0 %d\n",
+ __func__, rc);
+ return rc;
+ }
+ pr_debug("Turned OFF camera clocks\n");
+ return 0;
+
+}
+static int msm_early_cam_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ CDBG("%s: pdev %pK device id = %d\n", __func__, pdev, pdev->id);
+
+ /* Vote for Early camera if enabled */
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_ISPIF,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_VFE0,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ new_early_cam_dev = kzalloc(sizeof(struct early_cam_device),
+ GFP_KERNEL);
+ if (!new_early_cam_dev)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+
+ rc = msm_camera_get_clk_info_and_rates(pdev,
+ &new_early_cam_dev->early_cam_clk_info,
+ &new_early_cam_dev->early_cam_clk,
+ &new_early_cam_dev->early_cam_clk_rates,
+ &new_early_cam_dev->num_clk_cases,
+ &new_early_cam_dev->num_clk);
+ if (rc < 0) {
+ pr_err("%s: msm_early_cam_get_clk_info() failed", __func__);
+ kfree(new_early_cam_dev);
+ return -EFAULT;
+ }
+
+ new_early_cam_dev->ref_count = 0;
+ new_early_cam_dev->pdev = pdev;
+
+ rc = msm_camera_get_dt_vreg_data(
+ new_early_cam_dev->pdev->dev.of_node,
+ &(new_early_cam_dev->early_cam_vreg),
+ &(new_early_cam_dev->regulator_count));
+ if (rc < 0) {
+ pr_err("%s: msm_camera_get_dt_vreg_data fail\n", __func__);
+ rc = -EFAULT;
+ goto early_cam_release_mem;
+ }
+
+ if ((new_early_cam_dev->regulator_count < 0) ||
+ (new_early_cam_dev->regulator_count > MAX_REGULATOR)) {
+ pr_err("%s: invalid reg count = %d, max is %d\n", __func__,
+ new_early_cam_dev->regulator_count, MAX_REGULATOR);
+ rc = -EFAULT;
+ goto early_cam_invalid_vreg_data;
+ }
+
+ rc = msm_camera_config_vreg(&new_early_cam_dev->pdev->dev,
+ new_early_cam_dev->early_cam_vreg,
+ new_early_cam_dev->regulator_count,
+ NULL,
+ 0,
+ &new_early_cam_dev->early_cam_reg_ptr[0], 1);
+ if (rc < 0)
+ pr_err("%s:%d early_cam config_vreg failed\n", __func__,
+ __LINE__);
+
+ rc = msm_camera_enable_vreg(&new_early_cam_dev->pdev->dev,
+ new_early_cam_dev->early_cam_vreg,
+ new_early_cam_dev->regulator_count,
+ NULL,
+ 0,
+ &new_early_cam_dev->early_cam_reg_ptr[0], 1);
+ if (rc < 0)
+ pr_err("%s:%d early_cam enable_vreg failed\n", __func__,
+ __LINE__);
+
+ rc = msm_camera_clk_enable(&new_early_cam_dev->pdev->dev,
+ new_early_cam_dev->early_cam_clk_info,
+ new_early_cam_dev->early_cam_clk,
+ new_early_cam_dev->num_clk, true);
+
+ if (rc < 0) {
+ pr_err("%s: clk enable failed %d\n", __func__, rc);
+ rc = 0;
+ goto early_cam_release_mem;
+ }
+
+ platform_set_drvdata(pdev, new_early_cam_dev);
+
+ return 0;
+
+early_cam_invalid_vreg_data:
+ kfree(new_early_cam_dev->early_cam_vreg);
+early_cam_release_mem:
+ kfree(new_early_cam_dev);
+ new_early_cam_dev = NULL;
+ return rc;
+}
+
+static int msm_early_cam_exit(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int __init msm_early_cam_init_module(void)
+{
+ return platform_driver_register(&msm_early_camera_driver);
+}
+
+static void __exit msm_early_cam_exit_module(void)
+{
+ kfree(new_early_cam_dev);
+ platform_driver_unregister(&msm_early_camera_driver);
+}
+
+static const struct of_device_id msm_early_camera_match_table[] = {
+ { .compatible = "qcom,early-cam" },
+ {},
+};
+
+static struct platform_driver msm_early_camera_driver = {
+ .probe = msm_early_cam_probe,
+ .remove = msm_early_cam_exit,
+ .driver = {
+ .name = MSM_EARLY_CAM_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_early_camera_match_table,
+ },
+};
+
+MODULE_DEVICE_TABLE(of, msm_early_camera_match_table);
+
+module_init(msm_early_cam_init_module);
+module_exit(msm_early_cam_exit_module);
+MODULE_DESCRIPTION("MSM early camera driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.h b/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.h
new file mode 100644
index 000000000000..a40ab2d1ebc3
--- /dev/null
+++ b/drivers/media/platform/msm/ais/sensor/cci/msm_early_cam.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_EARLY_CAM_H
+#define MSM_EARLY_CAM_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <linux/workqueue.h>
+#include <media/ais/msm_ais_sensor.h>
+#include <soc/qcom/ais.h>
+#include "msm_sd.h"
+#include "cam_soc_api.h"
+
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+#define TRUE 1
+#define FALSE 0
+
+
+enum msm_early_cam_state_t {
+ STATE_DISABLED,
+ STATE_ENABLED,
+};
+
+struct early_cam_device {
+ struct platform_device *pdev;
+ uint8_t ref_count;
+ enum msm_early_cam_state_t early_cam_state;
+ size_t num_clk;
+ size_t num_clk_cases;
+ struct clk **early_cam_clk;
+ uint32_t **early_cam_clk_rates;
+ struct msm_cam_clk_info *early_cam_clk_info;
+ struct camera_vreg_t *early_cam_vreg;
+ struct regulator *early_cam_reg_ptr[MAX_REGULATOR];
+ int32_t regulator_count;
+};
+
+int msm_early_cam_disable_clocks(void);
+#endif
diff --git a/drivers/media/platform/msm/ais/sensor/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/msm_sensor_init.c b/drivers/media/platform/msm/ais/sensor/msm_sensor_init.c
index c3943be78226..ffbf963e819e 100644
--- a/drivers/media/platform/msm/ais/sensor/msm_sensor_init.c
+++ b/drivers/media/platform/msm/ais/sensor/msm_sensor_init.c
@@ -16,11 +16,17 @@
#include "msm_sensor_driver.h"
#include "msm_sensor.h"
#include "msm_sd.h"
+#include "msm_camera_io_util.h"
+#include "msm_early_cam.h"
/* Logging macro */
#undef CDBG
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#define EARLY_CAMERA_SIGNAL_DONE 0xa5a5a5a5
+#define EARLY_CAMERA_SIGNAL_DISABLED 0
+
+static bool early_camera_clock_off;
static struct msm_sensor_init_t *s_init;
static int msm_sensor_wait_for_probe_done(struct msm_sensor_init_t *s_init)
@@ -42,10 +48,14 @@ static int msm_sensor_wait_for_probe_done(struct msm_sensor_init_t *s_init)
return rc;
}
+#define MMSS_A_VFE_0_SPARE 0xC84
+
/* Static function definition */
int32_t msm_sensor_driver_cmd(struct msm_sensor_init_t *s_init, void *arg)
{
int32_t rc = 0;
+ u32 val = 0;
+ void __iomem *base;
struct sensor_init_cfg_data *cfg = (struct sensor_init_cfg_data *)arg;
/* Validate input parameters */
@@ -68,6 +78,28 @@ int32_t msm_sensor_driver_cmd(struct msm_sensor_init_t *s_init, void *arg)
break;
case CFG_SINIT_PROBE_DONE:
+ if (early_camera_clock_off == false) {
+ base = ioremap(0x00A10000, 0x1000);
+ val = msm_camera_io_r_mb(base + MMSS_A_VFE_0_SPARE);
+ while (val != EARLY_CAMERA_SIGNAL_DONE) {
+ if (val == EARLY_CAMERA_SIGNAL_DISABLED)
+ break;
+ msleep(1000);
+ val = msm_camera_io_r_mb(
+ base + MMSS_A_VFE_0_SPARE);
+ pr_err("Waiting for signal from LK val = %u\n",
+ val);
+ }
+ rc = msm_early_cam_disable_clocks();
+ if (rc < 0) {
+ pr_err("Failed to disable early camera :%d\n",
+ rc);
+ } else {
+ early_camera_clock_off = true;
+ pr_debug("Voted OFF early camera clocks\n");
+ }
+ }
+
s_init->module_init_status = 1;
wake_up(&s_init->state_wait);
break;
@@ -99,6 +131,7 @@ static int __init msm_sensor_init_module(void)
mutex_init(&s_init->imutex);
init_waitqueue_head(&s_init->state_wait);
+ early_camera_clock_off = false;
return ret;
}
diff --git a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c
index 236660dca3fb..3dccb73d9de4 100644
--- a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c
+++ b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c
@@ -623,11 +623,13 @@ static long msm_ois_subdev_ioctl(struct v4l2_subdev *sd,
pr_err("o_ctrl->i2c_client.i2c_func_tbl NULL\n");
return -EINVAL;
}
+ mutex_lock(o_ctrl->ois_mutex);
rc = msm_ois_power_down(o_ctrl);
if (rc < 0) {
pr_err("%s:%d OIS Power down failed\n",
__func__, __LINE__);
}
+ mutex_unlock(o_ctrl->ois_mutex);
return msm_ois_close(sd, NULL);
default:
return -ENOIOCTLCMD;
@@ -781,6 +783,7 @@ static long msm_ois_subdev_do_ioctl(
u32 = (struct msm_ois_cfg_data32 *)arg;
parg = arg;
+
switch (cmd) {
case VIDIOC_MSM_OIS_CFG32:
cmd = VIDIOC_MSM_OIS_CFG;
@@ -818,7 +821,6 @@ static long msm_ois_subdev_do_ioctl(
settings.reg_setting =
compat_ptr(settings32.reg_setting);
- ois_data.cfgtype = u32->cfgtype;
ois_data.cfg.settings = &settings;
parg = &ois_data;
break;
@@ -826,6 +828,10 @@ static long msm_ois_subdev_do_ioctl(
parg = &ois_data;
break;
}
+ break;
+ case VIDIOC_MSM_OIS_CFG:
+ pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd);
+ return -EINVAL;
}
rc = msm_ois_subdev_ioctl(sd, cmd, parg);
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index df0664b496ba..aeeb5cae3096 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -623,6 +623,7 @@ static int camera_v4l2_open(struct file *filep)
unsigned long opn_idx, idx;
BUG_ON(!pvdev);
+ mutex_lock(&pvdev->video_drvdata_mutex);
rc = camera_v4l2_fh_open(filep);
if (rc < 0) {
pr_err("%s : camera_v4l2_fh_open failed Line %d rc %d\n",
@@ -693,6 +694,7 @@ static int camera_v4l2_open(struct file *filep)
idx |= (1 << find_first_zero_bit((const unsigned long *)&opn_idx,
MSM_CAMERA_STREAM_CNT_BITS));
atomic_cmpxchg(&pvdev->opened, opn_idx, idx);
+ mutex_unlock(&pvdev->video_drvdata_mutex);
return rc;
@@ -707,6 +709,7 @@ stream_fail:
vb2_q_fail:
camera_v4l2_fh_release(filep);
fh_open_fail:
+ mutex_unlock(&pvdev->video_drvdata_mutex);
return rc;
}
@@ -737,6 +740,7 @@ static int camera_v4l2_close(struct file *filep)
if (WARN_ON(!session))
return -EIO;
+ mutex_lock(&pvdev->video_drvdata_mutex);
mutex_lock(&session->close_lock);
opn_idx = atomic_read(&pvdev->opened);
mask = (1 << sp->stream_id);
@@ -778,6 +782,7 @@ static int camera_v4l2_close(struct file *filep)
}
camera_v4l2_fh_release(filep);
+ mutex_unlock(&pvdev->video_drvdata_mutex);
return 0;
}
@@ -924,6 +929,7 @@ int camera_init_v4l2(struct device *dev, unsigned int *session)
*session = pvdev->vdev->num;
atomic_set(&pvdev->opened, 0);
+ mutex_init(&pvdev->video_drvdata_mutex);
video_set_drvdata(pvdev->vdev, pvdev);
device_init_wakeup(&pvdev->vdev->dev, 1);
goto init_end;
diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c
index 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/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
index 63c3595a3f85..d881b4aea48f 100644
--- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
+++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
@@ -434,6 +434,7 @@ static int msm_fd_open(struct file *file)
ctx->vb2_q.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
ctx->vb2_q.io_modes = VB2_USERPTR;
ctx->vb2_q.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ mutex_init(&ctx->lock);
ret = vb2_queue_init(&ctx->vb2_q);
if (ret < 0) {
dev_err(device->dev, "Error queue init\n");
@@ -484,7 +485,9 @@ static int msm_fd_release(struct file *file)
msm_cpp_vbif_register_error_handler((void *)ctx,
VBIF_CLIENT_FD, NULL);
+ mutex_lock(&ctx->lock);
vb2_queue_release(&ctx->vb2_q);
+ mutex_unlock(&ctx->lock);
vfree(ctx->stats);
@@ -514,7 +517,9 @@ static unsigned int msm_fd_poll(struct file *file,
struct fd_ctx *ctx = msm_fd_ctx_from_fh(file->private_data);
unsigned int ret;
+ mutex_lock(&ctx->lock);
ret = vb2_poll(&ctx->vb2_q, file, wait);
+ mutex_unlock(&ctx->lock);
if (atomic_read(&ctx->subscribed_for_event)) {
poll_wait(file, &ctx->fh.wait, wait);
@@ -752,9 +757,9 @@ static int msm_fd_reqbufs(struct file *file,
int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- mutex_lock(&ctx->fd_device->recovery_lock);
+ mutex_lock(&ctx->lock);
ret = vb2_reqbufs(&ctx->vb2_q, req);
- mutex_unlock(&ctx->fd_device->recovery_lock);
+ mutex_unlock(&ctx->lock);
return ret;
}
@@ -770,9 +775,9 @@ static int msm_fd_qbuf(struct file *file, void *fh,
int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- mutex_lock(&ctx->fd_device->recovery_lock);
+ mutex_lock(&ctx->lock);
ret = vb2_qbuf(&ctx->vb2_q, pb);
- mutex_unlock(&ctx->fd_device->recovery_lock);
+ mutex_unlock(&ctx->lock);
return ret;
}
@@ -789,9 +794,9 @@ static int msm_fd_dqbuf(struct file *file,
int ret;
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
- mutex_lock(&ctx->fd_device->recovery_lock);
+ mutex_lock(&ctx->lock);
ret = vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK);
- mutex_unlock(&ctx->fd_device->recovery_lock);
+ mutex_unlock(&ctx->lock);
return ret;
}
@@ -807,7 +812,9 @@ static int msm_fd_streamon(struct file *file,
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
int ret;
+ mutex_lock(&ctx->lock);
ret = vb2_streamon(&ctx->vb2_q, buf_type);
+ mutex_unlock(&ctx->lock);
if (ret < 0)
dev_err(ctx->fd_device->dev, "Stream on fails\n");
@@ -826,7 +833,9 @@ static int msm_fd_streamoff(struct file *file,
struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
int ret;
+ mutex_lock(&ctx->lock);
ret = vb2_streamoff(&ctx->vb2_q, buf_type);
+ mutex_unlock(&ctx->lock);
if (ret < 0)
dev_err(ctx->fd_device->dev, "Stream off fails\n");
diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h
index 6eae2b8d56fb..2b81e5b9ece3 100644
--- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h
+++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -174,6 +174,7 @@ struct fd_ctx {
struct msm_fd_mem_pool mem_pool;
struct msm_fd_stats *stats;
struct msm_fd_buf_handle work_buf;
+ struct mutex lock;
};
/*
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
index 23f936258660..22eb86f4f875 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
@@ -53,7 +53,6 @@ MODULE_DEVICE_TABLE(of, msm_vfe_dt_match);
#define MAX_OVERFLOW_COUNTERS 29
#define OVERFLOW_LENGTH 1024
#define OVERFLOW_BUFFER_LENGTH 64
-static char stat_line[OVERFLOW_LENGTH];
struct msm_isp_statistics stats;
struct msm_isp_ub_info ub_info;
@@ -113,19 +112,30 @@ static int vfe_debugfs_statistics_open(struct inode *inode, struct file *file)
return 0;
}
-static ssize_t vfe_debugfs_statistics_read(struct file *t_file, char *t_char,
- size_t t_size_t, loff_t *t_loff_t)
+static ssize_t vfe_debugfs_statistics_read(struct file *t_file,
+ char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
{
int i;
+ size_t rc;
uint64_t *ptr;
char buffer[OVERFLOW_BUFFER_LENGTH] = {0};
+ char *stat_line;
struct vfe_device *vfe_dev = (struct vfe_device *)
t_file->private_data;
- struct msm_isp_statistics *stats = vfe_dev->stats;
+ struct msm_isp_statistics *stats;
- memset(stat_line, 0, sizeof(stat_line));
+ stat_line = kzalloc(OVERFLOW_LENGTH, GFP_KERNEL);
+ if (!stat_line)
+ return -ENOMEM;
+ spin_lock(&vfe_dev->common_data->common_dev_data_lock);
+ stats = vfe_dev->stats;
msm_isp_util_get_bandwidth_stats(vfe_dev, stats);
+ spin_unlock(&vfe_dev->common_data->common_dev_data_lock);
ptr = (uint64_t *)(stats);
+ if (MAX_OVERFLOW_COUNTERS > OVERFLOW_LENGTH) {
+ kfree(stat_line);
+ return -EINVAL;
+ }
for (i = 0; i < MAX_OVERFLOW_COUNTERS; i++) {
strlcat(stat_line, stats_str[i], sizeof(stat_line));
strlcat(stat_line, " ", sizeof(stat_line));
@@ -133,8 +143,10 @@ static ssize_t vfe_debugfs_statistics_read(struct file *t_file, char *t_char,
strlcat(stat_line, buffer, sizeof(stat_line));
strlcat(stat_line, "\r\n", sizeof(stat_line));
}
- return simple_read_from_buffer(t_char, t_size_t,
+ rc = simple_read_from_buffer(t_char, t_size_t,
t_loff_t, stat_line, strlen(stat_line));
+ kfree(stat_line);
+ return rc;
}
static ssize_t vfe_debugfs_statistics_write(struct file *t_file,
@@ -142,8 +154,12 @@ static ssize_t vfe_debugfs_statistics_write(struct file *t_file,
{
struct vfe_device *vfe_dev = (struct vfe_device *)
t_file->private_data;
- struct msm_isp_statistics *stats = vfe_dev->stats;
+ struct msm_isp_statistics *stats;
+
+ spin_lock(&vfe_dev->common_data->common_dev_data_lock);
+ stats = vfe_dev->stats;
memset(stats, 0, sizeof(struct msm_isp_statistics));
+ spin_unlock(&vfe_dev->common_data->common_dev_data_lock);
return sizeof(struct msm_isp_statistics);
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index e87f2414a879..92b1f2ea871b 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -196,7 +196,7 @@ uint32_t msm_isp_get_framedrop_period(
return 32;
break;
case SKIP_ALL:
- return 1;
+ return SKIP_ALL;
default:
return 1;
}
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index 3f900ded090a..9c3bd7b41ce9 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -1025,6 +1025,9 @@ static void msm_ispif_config_stereo(struct ispif_device *ispif,
enum msm_ispif_vfe_intf vfe_intf;
uint32_t stereo_3d_threshold = STEREO_DEFAULT_3D_THRESHOLD;
+ if (params->num > MAX_PARAM_ENTRIES)
+ return;
+
for (i = 0; i < params->num; i++) {
vfe_intf = params->entries[i].vfe_intf;
if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 9cb7d5299ef8..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;
@@ -287,6 +288,7 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id)
return;
while (1) {
+ unsigned long wl_flags;
if (try_count > 5) {
pr_err("%s : not able to delete stream %d\n",
@@ -294,18 +296,20 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id)
break;
}
- write_lock(&session->stream_rwlock);
+ write_lock_irqsave(&session->stream_rwlock, wl_flags);
try_count++;
stream = msm_queue_find(&session->stream_q, struct msm_stream,
list, __msm_queue_find_stream, &stream_id);
if (!stream) {
- write_unlock(&session->stream_rwlock);
+ write_unlock_irqrestore(&session->stream_rwlock,
+ wl_flags);
return;
}
if (msm_vb2_get_stream_state(stream) != 1) {
- write_unlock(&session->stream_rwlock);
+ write_unlock_irqrestore(&session->stream_rwlock,
+ wl_flags);
continue;
}
@@ -315,7 +319,7 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id)
kfree(stream);
stream = NULL;
spin_unlock_irqrestore(&(session->stream_q.lock), flags);
- write_unlock(&session->stream_rwlock);
+ write_unlock_irqrestore(&session->stream_rwlock, wl_flags);
break;
}
@@ -385,6 +389,11 @@ static void msm_add_sd_in_position(struct msm_sd_subdev *msm_subdev,
struct msm_sd_subdev *temp_sd;
list_for_each_entry(temp_sd, sd_list, list) {
+ if (temp_sd == msm_subdev) {
+ pr_err("%s :Fail to add the same sd %d\n",
+ __func__, __LINE__);
+ return;
+ }
if (msm_subdev->close_seq < temp_sd->close_seq) {
list_add_tail(&msm_subdev->list, &temp_sd->list);
return;
@@ -401,7 +410,9 @@ int msm_sd_register(struct msm_sd_subdev *msm_subdev)
if (WARN_ON(!msm_v4l2_dev) || WARN_ON(!msm_v4l2_dev->dev))
return -EIO;
+ mutex_lock(&ordered_sd_mtx);
msm_add_sd_in_position(msm_subdev, &ordered_sd_list);
+ mutex_unlock(&ordered_sd_mtx);
return __msm_sd_register_subdev(&msm_subdev->sd);
}
EXPORT_SYMBOL(msm_sd_register);
@@ -735,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;
@@ -803,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;
@@ -992,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();
@@ -1350,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/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
index 719b14226067..e271c7fcd1b6 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
@@ -47,22 +47,23 @@ int msm_vb2_buf_init(struct vb2_buffer *vb)
struct msm_session *session;
struct msm_vb2_buffer *msm_vb2_buf;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ unsigned long rl_flags;
session = msm_get_session_from_vb2q(vb->vb2_queue);
if (IS_ERR_OR_NULL(session))
return -EINVAL;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s: Couldn't find stream\n", __func__);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return -EINVAL;
}
msm_vb2_buf = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf);
msm_vb2_buf->in_freeq = 0;
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return 0;
}
@@ -71,7 +72,7 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb)
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
struct msm_session *session;
- unsigned long flags;
+ unsigned long flags, rl_flags;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
msm_vb2 = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf);
@@ -84,19 +85,19 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb)
if (IS_ERR_OR_NULL(session))
return;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s:%d] NULL stream", __func__, __LINE__);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return;
}
spin_lock_irqsave(&stream->stream_lock, flags);
list_add_tail(&msm_vb2->list, &stream->queued_list);
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
}
static void msm_vb2_buf_finish(struct vb2_buffer *vb)
@@ -104,26 +105,26 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb)
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
struct msm_session *session;
- unsigned long flags;
+ unsigned long flags, rl_flags;
struct msm_vb2_buffer *msm_vb2_entry, *temp;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
msm_vb2 = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf);
if (!msm_vb2) {
pr_err("%s:%d] vb2_buf NULL", __func__, __LINE__);
- return;
+ return;
}
session = msm_get_session_from_vb2q(vb->vb2_queue);
if (IS_ERR_OR_NULL(session))
return;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s:%d] NULL stream", __func__, __LINE__);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return;
}
@@ -136,7 +137,7 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb)
}
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return;
}
@@ -145,19 +146,19 @@ static void msm_vb2_stop_stream(struct vb2_queue *q)
struct msm_vb2_buffer *msm_vb2, *temp;
struct msm_stream *stream;
struct msm_session *session;
- unsigned long flags;
+ unsigned long flags, rl_flags;
struct vb2_v4l2_buffer *vb2_v4l2_buf;
session = msm_get_session_from_vb2q(q);
if (IS_ERR_OR_NULL(session))
return;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream_from_vb2q(q);
if (!stream) {
pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return;
}
@@ -177,7 +178,7 @@ static void msm_vb2_stop_stream(struct vb2_queue *q)
msm_vb2->in_freeq = 0;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
}
int msm_vb2_get_stream_state(struct msm_stream *stream)
@@ -255,17 +256,17 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id,
struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
- unsigned long flags;
+ unsigned long flags, rl_flags;
session = msm_get_session(session_id);
if (IS_ERR_OR_NULL(session))
return NULL;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream(session, stream_id);
if (IS_ERR_OR_NULL(stream)) {
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return NULL;
}
@@ -291,7 +292,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id,
vb2_v4l2_buf = NULL;
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return vb2_v4l2_buf;
}
@@ -302,18 +303,18 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
- unsigned long flags;
+ unsigned long flags, rl_flags;
session = msm_get_session(session_id);
if (IS_ERR_OR_NULL(session))
return NULL;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream(session, stream_id);
if (IS_ERR_OR_NULL(stream)) {
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return NULL;
}
@@ -337,7 +338,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
vb2_v4l2_buf = NULL;
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return vb2_v4l2_buf;
}
@@ -349,17 +350,17 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
struct msm_vb2_buffer *msm_vb2;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
int rc = 0;
- unsigned long flags;
+ unsigned long flags, rl_flags;
session = msm_get_session(session_id);
if (IS_ERR_OR_NULL(session))
return -EINVAL;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream(session, stream_id);
if (IS_ERR_OR_NULL(stream)) {
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return -EINVAL;
}
@@ -374,7 +375,8 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
pr_err("VB buffer is INVALID vb=%pK, ses_id=%d, str_id=%d\n",
vb, session_id, stream_id);
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock,
+ rl_flags);
return -EINVAL;
}
msm_vb2 =
@@ -391,7 +393,7 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
rc = -EINVAL;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return rc;
}
@@ -399,7 +401,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
unsigned int stream_id, uint32_t sequence,
struct timeval *ts, uint32_t reserved)
{
- unsigned long flags;
+ unsigned long flags, rl_flags;
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
struct msm_session *session;
@@ -410,11 +412,11 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
if (IS_ERR_OR_NULL(session))
return -EINVAL;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream(session, stream_id);
if (IS_ERR_OR_NULL(stream)) {
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return -EINVAL;
}
@@ -429,7 +431,8 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
pr_err("VB buffer is INVALID ses_id=%d, str_id=%d, vb=%pK\n",
session_id, stream_id, vb);
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock,
+ rl_flags);
return -EINVAL;
}
msm_vb2 =
@@ -450,7 +453,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
rc = -EINVAL;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return rc;
}
@@ -461,18 +464,18 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
- unsigned long flags;
+ unsigned long flags, rl_flags;
long rc = -EINVAL;
session = msm_get_session(session_id);
if (IS_ERR_OR_NULL(session))
return rc;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream(session, stream_id);
if (IS_ERR_OR_NULL(stream)) {
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return -EINVAL;
}
@@ -501,14 +504,14 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return rc;
}
EXPORT_SYMBOL(msm_vb2_return_buf_by_idx);
static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
{
- unsigned long flags;
+ unsigned long flags, rl_flags;
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
struct msm_session *session;
@@ -518,11 +521,11 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
if (IS_ERR_OR_NULL(session))
return -EINVAL;
- read_lock(&session->stream_rwlock);
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
stream = msm_get_stream(session, stream_id);
if (IS_ERR_OR_NULL(stream)) {
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return -EINVAL;
}
@@ -534,7 +537,7 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
msm_vb2->in_freeq = 0;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
- read_unlock(&session->stream_rwlock);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
return 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
index 223ddf39dce8..c77367ed1603 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
@@ -1213,6 +1213,9 @@ static long msm_flash_subdev_do_ioctl(
break;
}
break;
+ case VIDIOC_MSM_FLASH_CFG:
+ pr_err("invalid cmd 0x%x received\n", cmd);
+ return -EINVAL;
default:
return msm_flash_subdev_ioctl(sd, cmd, arg);
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
index 1a40aa1c78bd..6d9b0e987d0d 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,13 +17,15 @@
#undef CDBG
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args)
+#define MAX_I2C_ADDR_TYPE_SIZE (MSM_CAMERA_I2C_3B_ADDR + 1)
+#define MAX_I2C_DATA_TYPE_SIZE (MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA + 1)
int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
uint32_t addr, uint16_t *data,
enum msm_camera_i2c_data_type data_type)
{
int32_t rc = -EFAULT;
- unsigned char buf[client->addr_type+data_type];
+ unsigned char buf[MAX_I2C_ADDR_TYPE_SIZE + MAX_I2C_DATA_TYPE_SIZE];
struct msm_camera_cci_ctrl cci_ctrl;
if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
index 3f079fe2c173..457bd1730232 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -575,6 +575,8 @@ int msm_camera_get_dt_power_setting_data(struct device_node *of_node,
ps[i].seq_val = SENSOR_GPIO_CUSTOM1;
else if (!strcmp(seq_name, "sensor_gpio_custom2"))
ps[i].seq_val = SENSOR_GPIO_CUSTOM2;
+ else if (!strcmp(seq_name, "sensor_gpio_custom3"))
+ ps[i].seq_val = SENSOR_GPIO_CUSTOM3;
else
rc = -EILSEQ;
break;
@@ -1078,6 +1080,27 @@ int msm_camera_init_gpio_pin_tbl(struct device_node *of_node,
rc = 0;
}
+ rc = of_property_read_u32(of_node, "qcom,gpio-custom3", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-custom3 failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-custom3 invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM3] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_CUSTOM3] = 1;
+ CDBG("%s qcom,gpio-custom3 %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM3]);
+ } else {
+ rc = 0;
+ }
+
return rc;
ERROR:
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
index a41d7dba490e..57bc392f54fd 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -108,7 +108,11 @@ static int32_t msm_sensor_driver_create_i2c_v4l_subdev
s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name;
s_ctrl->sensordata->sensor_info->session_id = session_id;
s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3;
- msm_sd_register(&s_ctrl->msm_sd);
+ rc = msm_sd_register(&s_ctrl->msm_sd);
+ if (rc < 0) {
+ pr_err("failed: msm_sd_register rc %d", rc);
+ return rc;
+ }
msm_sensor_v4l2_subdev_fops = v4l2_subdev_fops;
#ifdef CONFIG_COMPAT
msm_sensor_v4l2_subdev_fops.compat_ioctl32 =
@@ -148,7 +152,11 @@ static int32_t msm_sensor_driver_create_v4l_subdev
s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR;
s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name;
s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3;
- msm_sd_register(&s_ctrl->msm_sd);
+ rc = msm_sd_register(&s_ctrl->msm_sd);
+ if (rc < 0) {
+ pr_err("failed: msm_sd_register rc %d", rc);
+ return rc;
+ }
msm_cam_copy_v4l2_subdev_fops(&msm_sensor_v4l2_subdev_fops);
#ifdef CONFIG_COMPAT
msm_sensor_v4l2_subdev_fops.compat_ioctl32 =
@@ -995,12 +1003,6 @@ CSID_TG:
pr_err("%s probe succeeded", slave_info->sensor_name);
- /*
- Set probe succeeded flag to 1 so that no other camera shall
- * probed on this slot
- */
- s_ctrl->is_probe_succeed = 1;
-
s_ctrl->bypass_video_node_creation =
slave_info->bypass_video_node_creation;
@@ -1048,6 +1050,11 @@ CSID_TG:
msm_sensor_fill_sensor_info(s_ctrl, probed_info, entity_name);
+ /*
+ * Set probe succeeded flag to 1 so that no other camera shall
+ * probed on this slot
+ */
+ s_ctrl->is_probe_succeed = 1;
return rc;
camera_power_down:
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
index bfb15846e73c..302a7b16bc26 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
@@ -615,11 +615,13 @@ static long msm_ois_subdev_ioctl(struct v4l2_subdev *sd,
pr_err("o_ctrl->i2c_client.i2c_func_tbl NULL\n");
return -EINVAL;
}
+ mutex_lock(o_ctrl->ois_mutex);
rc = msm_ois_power_down(o_ctrl);
if (rc < 0) {
pr_err("%s:%d OIS Power down failed\n",
__func__, __LINE__);
}
+ mutex_unlock(o_ctrl->ois_mutex);
return msm_ois_close(sd, NULL);
default:
return -ENOIOCTLCMD;
@@ -818,6 +820,10 @@ static long msm_ois_subdev_do_ioctl(
parg = &ois_data;
break;
}
+ break;
+ case VIDIOC_MSM_OIS_CFG:
+ pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd);
+ return -EINVAL;
}
rc = msm_ois_subdev_ioctl(sd, cmd, parg);
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index 2d2296893140..9f3e2cc3a72f 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -523,13 +523,17 @@ static ssize_t mpq_sdmx_log_level_write(struct file *fp,
int level;
struct mpq_demux *mpq_demux = fp->private_data;
- if (count >= 16)
+ if (count == 0 || count >= 16)
return -EINVAL;
- ret_count = simple_write_to_buffer(user_str, 16, position, user_buffer,
+ memset(user_str, '\0', sizeof(user_str));
+
+ ret_count = simple_write_to_buffer(user_str, 15, position, user_buffer,
count);
if (ret_count < 0)
return ret_count;
+ else if (ret_count == 0)
+ return -EINVAL;
ret = kstrtoint(user_str, 0, &level);
if (ret)
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.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index de4705c3d2eb..2eaae18bc2e9 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -533,11 +533,18 @@ static inline void save_v4l2_buffer(struct v4l2_buffer *b,
static int __map_and_update_binfo(struct msm_vidc_inst *inst,
struct buffer_info *binfo,
- struct v4l2_buffer *b, int i)
+ struct v4l2_buffer *b, u32 i)
{
int rc = 0;
struct msm_smem *same_fd_handle = NULL;
+ if (i >= VIDEO_MAX_PLANES) {
+ dprintk(VIDC_ERR, "Num planes exceeds max: %d, %d\n",
+ i, VIDEO_MAX_PLANES);
+ rc = -EINVAL;
+ goto exit;
+ }
+
same_fd_handle = get_same_fd_buffer(
inst, b->m.planes[i].reserved[0]);
@@ -558,6 +565,7 @@ static int __map_and_update_binfo(struct msm_vidc_inst *inst,
b->m.planes[i].m.userptr = binfo->device_addr[i];
}
+exit:
return rc;
}
@@ -565,7 +573,8 @@ static int __handle_fw_referenced_buffers(struct msm_vidc_inst *inst,
struct buffer_info *binfo,
struct v4l2_buffer *b)
{
- int i = 0, rc = 0;
+ int rc = 0;
+ u32 i = 0;
if (EXTRADATA_IDX(b->length)) {
i = EXTRADATA_IDX(b->length);
@@ -583,8 +592,8 @@ int map_and_register_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
{
struct buffer_info *binfo = NULL;
struct buffer_info *temp = NULL, *iterator = NULL;
- int plane = 0;
- int i = 0, rc = 0;
+ int plane = 0, rc = 0;
+ u32 i = 0;
if (!b || !inst) {
dprintk(VIDC_ERR, "%s: invalid input\n", __func__);
@@ -1416,10 +1425,6 @@ void *msm_vidc_open(int core_id, int session_type)
setup_event_queue(inst, &core->vdev[session_type].vdev);
- mutex_lock(&core->lock);
- list_add_tail(&inst->list, &core->instances);
- mutex_unlock(&core->lock);
-
rc = msm_comm_try_state(inst, MSM_VIDC_CORE_INIT_DONE);
if (rc) {
dprintk(VIDC_ERR,
@@ -1433,15 +1438,15 @@ void *msm_vidc_open(int core_id, int session_type)
goto fail_init;
}
+ mutex_lock(&core->lock);
+ list_add_tail(&inst->list, &core->instances);
+ mutex_unlock(&core->lock);
+
inst->debugfs_root =
msm_vidc_debugfs_init_inst(inst, core->debugfs_root);
return inst;
fail_init:
- mutex_lock(&core->lock);
- list_del(&inst->list);
- mutex_unlock(&core->lock);
-
v4l2_fh_del(&inst->event_handler);
v4l2_fh_exit(&inst->event_handler);
vb2_queue_release(&inst->bufq[OUTPUT_PORT].vb2_bufq);
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/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index 4a608cbe0fdb..9c6fc09b88e0 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -1098,10 +1098,10 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
struct s5p_jpeg_ctx *ctx)
{
int c, components = 0, notfound, n_dht = 0, n_dqt = 0;
- unsigned int height, width, word, subsampling = 0, sos = 0, sof = 0,
- sof_len = 0;
- unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER],
- dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER];
+ unsigned int height = 0, width = 0, word, subsampling = 0;
+ unsigned int sos = 0, sof = 0, sof_len = 0;
+ unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER];
+ unsigned int dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER];
long length;
struct s5p_jpeg_buffer jpeg_buffer;
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 65f80b8b9f7a..eb9e7feb9b13 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -1629,7 +1629,7 @@ static void imon_incoming_packet(struct imon_context *ictx,
if (kc == KEY_KEYBOARD && !ictx->release_code) {
ictx->last_keycode = kc;
if (!nomouse) {
- ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1;
+ ictx->pad_mouse = !ictx->pad_mouse;
dev_dbg(dev, "toggling to %s mode\n",
ictx->pad_mouse ? "mouse" : "keyboard");
spin_unlock_irqrestore(&ictx->kc_lock, flags);
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index a32659fcd266..efc21b1da211 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -254,7 +254,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
return 0;
case LIRC_GET_REC_RESOLUTION:
- val = dev->rx_resolution;
+ val = dev->rx_resolution / 1000;
break;
case LIRC_SET_WIDEBAND_RECEIVER:
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index d86795bf9453..52f75b1faec0 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -565,6 +565,14 @@ config QPNP_MISC
peripheral. The MISC peripheral holds the USB ID interrupt
and the driver provides an API to check if this interrupt
is available on the current PMIC chip.
+
+config UID_SYS_STATS_DEBUG
+ bool "Per-TASK statistics"
+ depends on UID_SYS_STATS
+ default n
+ help
+ Per TASK based io statistics exported to /proc/uid_io
+
config MEMORY_STATE_TIME
tristate "Memory freq/bandwidth time statistics"
depends on PROFILING
diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c
index 65fed7146e9b..cc91f7b3d90c 100644
--- a/drivers/misc/enclosure.c
+++ b/drivers/misc/enclosure.c
@@ -375,6 +375,7 @@ int enclosure_add_device(struct enclosure_device *edev, int component,
struct device *dev)
{
struct enclosure_component *cdev;
+ int err;
if (!edev || component >= edev->components)
return -EINVAL;
@@ -384,12 +385,17 @@ int enclosure_add_device(struct enclosure_device *edev, int component,
if (cdev->dev == dev)
return -EEXIST;
- if (cdev->dev)
+ if (cdev->dev) {
enclosure_remove_links(cdev);
-
- put_device(cdev->dev);
+ put_device(cdev->dev);
+ }
cdev->dev = get_device(dev);
- return enclosure_add_links(cdev);
+ err = enclosure_add_links(cdev);
+ if (err) {
+ put_device(cdev->dev);
+ cdev->dev = NULL;
+ }
+ return err;
}
EXPORT_SYMBOL_GPL(enclosure_add_device);
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index a2661381ddfc..d2774197fe58 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -125,6 +125,11 @@
#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */
#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */
+#define MEI_DEV_ID_LBG 0xA1BA /* Lewisburg (SPT) */
+
+#define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */
+#define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */
+
/*
* MEI HW Section
*/
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 01e20384ac44..adab5bbb642a 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -86,10 +86,14 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_sps_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_sps_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, mei_me_pch8_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)},
+
/* required last entry */
{0, }
};
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(&param, 0, sizeof(param));
+
if (!audio || !buf_node) {
pr_err("%s NULL pointer audio=[0x%pK], buf_node=[0x%pK]\n",
__func__, audio, buf_node);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 7bc5b5ad1122..cf897947fff2 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -8434,9 +8434,10 @@ out:
*/
static int qseecom_check_whitelist_feature(void)
{
- int version = scm_get_feat_version(FEATURE_ID_WHITELIST);
+ u64 version = 0;
+ int ret = scm_get_feat_version(FEATURE_ID_WHITELIST, &version);
- return version >= MAKE_WHITELIST_VERSION(1, 0, 0);
+ return (ret == 0) && (version >= MAKE_WHITELIST_VERSION(1, 0, 0));
}
static int qseecom_probe(struct platform_device *pdev)
diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c
index 3c9d311106cd..031320e51522 100644
--- a/drivers/misc/uid_sys_stats.c
+++ b/drivers/misc/uid_sys_stats.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/profile.h>
#include <linux/rtmutex.h>
@@ -52,6 +53,15 @@ struct io_stats {
#define UID_STATE_DEAD_TASKS 4
#define UID_STATE_SIZE 5
+#define MAX_TASK_COMM_LEN 256
+
+struct task_entry {
+ char comm[MAX_TASK_COMM_LEN];
+ pid_t pid;
+ struct io_stats io[UID_STATE_SIZE];
+ struct hlist_node hash;
+};
+
struct uid_entry {
uid_t uid;
cputime_t utime;
@@ -61,8 +71,231 @@ struct uid_entry {
int state;
struct io_stats io[UID_STATE_SIZE];
struct hlist_node hash;
+#ifdef CONFIG_UID_SYS_STATS_DEBUG
+ DECLARE_HASHTABLE(task_entries, UID_HASH_BITS);
+#endif
};
+static u64 compute_write_bytes(struct task_struct *task)
+{
+ if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
+ return 0;
+
+ return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
+}
+
+static void compute_io_bucket_stats(struct io_stats *io_bucket,
+ struct io_stats *io_curr,
+ struct io_stats *io_last,
+ struct io_stats *io_dead)
+{
+ /* tasks could switch to another uid group, but its io_last in the
+ * previous uid group could still be positive.
+ * therefore before each update, do an overflow check first
+ */
+ int64_t delta;
+
+ delta = io_curr->read_bytes + io_dead->read_bytes -
+ io_last->read_bytes;
+ io_bucket->read_bytes += delta > 0 ? delta : 0;
+ delta = io_curr->write_bytes + io_dead->write_bytes -
+ io_last->write_bytes;
+ io_bucket->write_bytes += delta > 0 ? delta : 0;
+ delta = io_curr->rchar + io_dead->rchar - io_last->rchar;
+ io_bucket->rchar += delta > 0 ? delta : 0;
+ delta = io_curr->wchar + io_dead->wchar - io_last->wchar;
+ io_bucket->wchar += delta > 0 ? delta : 0;
+ delta = io_curr->fsync + io_dead->fsync - io_last->fsync;
+ io_bucket->fsync += delta > 0 ? delta : 0;
+
+ io_last->read_bytes = io_curr->read_bytes;
+ io_last->write_bytes = io_curr->write_bytes;
+ io_last->rchar = io_curr->rchar;
+ io_last->wchar = io_curr->wchar;
+ io_last->fsync = io_curr->fsync;
+
+ memset(io_dead, 0, sizeof(struct io_stats));
+}
+
+#ifdef CONFIG_UID_SYS_STATS_DEBUG
+static void get_full_task_comm(struct task_entry *task_entry,
+ struct task_struct *task)
+{
+ int i = 0, offset = 0, len = 0;
+ /* save one byte for terminating null character */
+ int unused_len = MAX_TASK_COMM_LEN - TASK_COMM_LEN - 1;
+ char buf[unused_len];
+ struct mm_struct *mm = task->mm;
+
+ /* fill the first TASK_COMM_LEN bytes with thread name */
+ get_task_comm(task_entry->comm, task);
+ i = strlen(task_entry->comm);
+ while (i < TASK_COMM_LEN)
+ task_entry->comm[i++] = ' ';
+
+ /* next the executable file name */
+ if (mm) {
+ down_read(&mm->mmap_sem);
+ if (mm->exe_file) {
+ char *pathname = d_path(&mm->exe_file->f_path, buf,
+ unused_len);
+
+ if (!IS_ERR(pathname)) {
+ len = strlcpy(task_entry->comm + i, pathname,
+ unused_len);
+ i += len;
+ task_entry->comm[i++] = ' ';
+ unused_len--;
+ }
+ }
+ up_read(&mm->mmap_sem);
+ }
+ unused_len -= len;
+
+ /* fill the rest with command line argument
+ * replace each null or new line character
+ * between args in argv with whitespace */
+ len = get_cmdline(task, buf, unused_len);
+ while (offset < len) {
+ if (buf[offset] != '\0' && buf[offset] != '\n')
+ task_entry->comm[i++] = buf[offset];
+ else
+ task_entry->comm[i++] = ' ';
+ offset++;
+ }
+
+ /* get rid of trailing whitespaces in case when arg is memset to
+ * zero before being reset in userspace
+ */
+ while (task_entry->comm[i-1] == ' ')
+ i--;
+ task_entry->comm[i] = '\0';
+}
+
+static struct task_entry *find_task_entry(struct uid_entry *uid_entry,
+ struct task_struct *task)
+{
+ struct task_entry *task_entry;
+
+ hash_for_each_possible(uid_entry->task_entries, task_entry, hash,
+ task->pid) {
+ if (task->pid == task_entry->pid) {
+ /* if thread name changed, update the entire command */
+ int len = strnchr(task_entry->comm, ' ', TASK_COMM_LEN)
+ - task_entry->comm;
+
+ if (strncmp(task_entry->comm, task->comm, len))
+ get_full_task_comm(task_entry, task);
+ return task_entry;
+ }
+ }
+ return NULL;
+}
+
+static struct task_entry *find_or_register_task(struct uid_entry *uid_entry,
+ struct task_struct *task)
+{
+ struct task_entry *task_entry;
+ pid_t pid = task->pid;
+
+ task_entry = find_task_entry(uid_entry, task);
+ if (task_entry)
+ return task_entry;
+
+ task_entry = kzalloc(sizeof(struct task_entry), GFP_ATOMIC);
+ if (!task_entry)
+ return NULL;
+
+ get_full_task_comm(task_entry, task);
+
+ task_entry->pid = pid;
+ hash_add(uid_entry->task_entries, &task_entry->hash, (unsigned int)pid);
+
+ return task_entry;
+}
+
+static void remove_uid_tasks(struct uid_entry *uid_entry)
+{
+ struct task_entry *task_entry;
+ unsigned long bkt_task;
+ struct hlist_node *tmp_task;
+
+ hash_for_each_safe(uid_entry->task_entries, bkt_task,
+ tmp_task, task_entry, hash) {
+ hash_del(&task_entry->hash);
+ kfree(task_entry);
+ }
+}
+
+static void set_io_uid_tasks_zero(struct uid_entry *uid_entry)
+{
+ struct task_entry *task_entry;
+ unsigned long bkt_task;
+
+ hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
+ memset(&task_entry->io[UID_STATE_TOTAL_CURR], 0,
+ sizeof(struct io_stats));
+ }
+}
+
+static void add_uid_tasks_io_stats(struct uid_entry *uid_entry,
+ struct task_struct *task, int slot)
+{
+ struct task_entry *task_entry = find_or_register_task(uid_entry, task);
+ struct io_stats *task_io_slot = &task_entry->io[slot];
+
+ task_io_slot->read_bytes += task->ioac.read_bytes;
+ task_io_slot->write_bytes += compute_write_bytes(task);
+ task_io_slot->rchar += task->ioac.rchar;
+ task_io_slot->wchar += task->ioac.wchar;
+ task_io_slot->fsync += task->ioac.syscfs;
+}
+
+static void compute_io_uid_tasks(struct uid_entry *uid_entry)
+{
+ struct task_entry *task_entry;
+ unsigned long bkt_task;
+
+ hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
+ compute_io_bucket_stats(&task_entry->io[uid_entry->state],
+ &task_entry->io[UID_STATE_TOTAL_CURR],
+ &task_entry->io[UID_STATE_TOTAL_LAST],
+ &task_entry->io[UID_STATE_DEAD_TASKS]);
+ }
+}
+
+static void show_io_uid_tasks(struct seq_file *m, struct uid_entry *uid_entry)
+{
+ struct task_entry *task_entry;
+ unsigned long bkt_task;
+
+ hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
+ /* Separated by comma because space exists in task comm */
+ seq_printf(m, "task,%s,%lu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n",
+ task_entry->comm,
+ (unsigned long)task_entry->pid,
+ task_entry->io[UID_STATE_FOREGROUND].rchar,
+ task_entry->io[UID_STATE_FOREGROUND].wchar,
+ task_entry->io[UID_STATE_FOREGROUND].read_bytes,
+ task_entry->io[UID_STATE_FOREGROUND].write_bytes,
+ task_entry->io[UID_STATE_BACKGROUND].rchar,
+ task_entry->io[UID_STATE_BACKGROUND].wchar,
+ task_entry->io[UID_STATE_BACKGROUND].read_bytes,
+ task_entry->io[UID_STATE_BACKGROUND].write_bytes,
+ task_entry->io[UID_STATE_FOREGROUND].fsync,
+ task_entry->io[UID_STATE_BACKGROUND].fsync);
+ }
+}
+#else
+static void remove_uid_tasks(struct uid_entry *uid_entry) {};
+static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) {};
+static void add_uid_tasks_io_stats(struct uid_entry *uid_entry,
+ struct task_struct *task, int slot) {};
+static void compute_io_uid_tasks(struct uid_entry *uid_entry) {};
+static void show_io_uid_tasks(struct seq_file *m,
+ struct uid_entry *uid_entry) {}
+#endif
+
static struct uid_entry *find_uid_entry(uid_t uid)
{
struct uid_entry *uid_entry;
@@ -86,7 +319,9 @@ static struct uid_entry *find_or_register_uid(uid_t uid)
return NULL;
uid_entry->uid = uid;
-
+#ifdef CONFIG_UID_SYS_STATS_DEBUG
+ hash_init(uid_entry->task_entries);
+#endif
hash_add(hash_table, &uid_entry->hash, uid);
return uid_entry;
@@ -192,6 +427,7 @@ static ssize_t uid_remove_write(struct file *file,
hash_for_each_possible_safe(hash_table, uid_entry, tmp,
hash, (uid_t)uid_start) {
if (uid_start == uid_entry->uid) {
+ remove_uid_tasks(uid_entry);
hash_del(&uid_entry->hash);
kfree(uid_entry);
}
@@ -208,13 +444,6 @@ static const struct file_operations uid_remove_fops = {
.write = uid_remove_write,
};
-static u64 compute_write_bytes(struct task_struct *task)
-{
- if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
- return 0;
-
- return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
-}
static void add_uid_io_stats(struct uid_entry *uid_entry,
struct task_struct *task, int slot)
@@ -226,28 +455,8 @@ static void add_uid_io_stats(struct uid_entry *uid_entry,
io_slot->rchar += task->ioac.rchar;
io_slot->wchar += task->ioac.wchar;
io_slot->fsync += task->ioac.syscfs;
-}
-static void compute_uid_io_bucket_stats(struct io_stats *io_bucket,
- struct io_stats *io_curr,
- struct io_stats *io_last,
- struct io_stats *io_dead)
-{
- io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes -
- io_last->read_bytes;
- io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes -
- io_last->write_bytes;
- io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar;
- io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar;
- io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync;
-
- io_last->read_bytes = io_curr->read_bytes;
- io_last->write_bytes = io_curr->write_bytes;
- io_last->rchar = io_curr->rchar;
- io_last->wchar = io_curr->wchar;
- io_last->fsync = io_curr->fsync;
-
- memset(io_dead, 0, sizeof(struct io_stats));
+ add_uid_tasks_io_stats(uid_entry, task, slot);
}
static void update_io_stats_all_locked(void)
@@ -258,9 +467,11 @@ static void update_io_stats_all_locked(void)
unsigned long bkt;
uid_t uid;
- hash_for_each(hash_table, bkt, uid_entry, hash)
+ hash_for_each(hash_table, bkt, uid_entry, hash) {
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
sizeof(struct io_stats));
+ set_io_uid_tasks_zero(uid_entry);
+ }
rcu_read_lock();
do_each_thread(temp, task) {
@@ -274,10 +485,11 @@ static void update_io_stats_all_locked(void)
rcu_read_unlock();
hash_for_each(hash_table, bkt, uid_entry, hash) {
- compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+ compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
&uid_entry->io[UID_STATE_TOTAL_CURR],
&uid_entry->io[UID_STATE_TOTAL_LAST],
&uid_entry->io[UID_STATE_DEAD_TASKS]);
+ compute_io_uid_tasks(uid_entry);
}
}
@@ -288,6 +500,7 @@ static void update_io_stats_uid_locked(struct uid_entry *uid_entry)
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
sizeof(struct io_stats));
+ set_io_uid_tasks_zero(uid_entry);
rcu_read_lock();
do_each_thread(temp, task) {
@@ -297,12 +510,14 @@ static void update_io_stats_uid_locked(struct uid_entry *uid_entry)
} while_each_thread(temp, task);
rcu_read_unlock();
- compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+ compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
&uid_entry->io[UID_STATE_TOTAL_CURR],
&uid_entry->io[UID_STATE_TOTAL_LAST],
&uid_entry->io[UID_STATE_DEAD_TASKS]);
+ compute_io_uid_tasks(uid_entry);
}
+
static int uid_io_show(struct seq_file *m, void *v)
{
struct uid_entry *uid_entry;
@@ -314,21 +529,22 @@ static int uid_io_show(struct seq_file *m, void *v)
hash_for_each(hash_table, bkt, uid_entry, hash) {
seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
- uid_entry->uid,
- uid_entry->io[UID_STATE_FOREGROUND].rchar,
- uid_entry->io[UID_STATE_FOREGROUND].wchar,
- uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
- uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
- uid_entry->io[UID_STATE_BACKGROUND].rchar,
- uid_entry->io[UID_STATE_BACKGROUND].wchar,
- uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
- uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
- uid_entry->io[UID_STATE_FOREGROUND].fsync,
- uid_entry->io[UID_STATE_BACKGROUND].fsync);
+ uid_entry->uid,
+ uid_entry->io[UID_STATE_FOREGROUND].rchar,
+ uid_entry->io[UID_STATE_FOREGROUND].wchar,
+ uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
+ uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
+ uid_entry->io[UID_STATE_BACKGROUND].rchar,
+ uid_entry->io[UID_STATE_BACKGROUND].wchar,
+ uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
+ uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
+ uid_entry->io[UID_STATE_FOREGROUND].fsync,
+ uid_entry->io[UID_STATE_BACKGROUND].fsync);
+
+ show_io_uid_tasks(m, uid_entry);
}
rt_mutex_unlock(&uid_lock);
-
return 0;
}
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 01e5502917f7..d39b4056c169 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1173,7 +1173,7 @@ idata_free:
cmd_done:
mmc_blk_put(md);
- if (card->cmdq_init)
+ if (card && card->cmdq_init)
wake_up(&card->host->cmdq_ctx.wait);
return err;
}
@@ -4623,6 +4623,10 @@ static int mmc_blk_probe(struct mmc_card *card)
dev_set_drvdata(&card->dev, md);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ mmc_set_bus_resume_policy(card->host, 1);
+#endif
+
if (mmc_add_disk(md))
goto out;
@@ -4666,6 +4670,9 @@ static void mmc_blk_remove(struct mmc_card *card)
pm_runtime_put_noidle(&card->dev);
mmc_blk_remove_req(md);
dev_set_drvdata(&card->dev, NULL);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ mmc_set_bus_resume_policy(card->host, 0);
+#endif
}
static int _mmc_blk_suspend(struct mmc_card *card, bool wait)
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 311f6d639d06..548a9e8b72ae 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -401,6 +401,7 @@ int mmc_add_card(struct mmc_card *card)
return ret;
mmc_card_set_present(card);
+ device_enable_async_suspend(&card->dev);
return 0;
}
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..c86a800fc203 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;
}
@@ -5009,7 +5015,7 @@ static int sdhci_msm_suspend_noirq(struct device *dev)
}
static const struct dev_pm_ops sdhci_msm_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume,
NULL)
.suspend_noirq = sdhci_msm_suspend_noirq,
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 0033fea0a800..5906bba0aeff 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
@@ -3059,8 +3054,20 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
return;
}
if (host->quirks2 &
- SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD)
+ SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD) {
+ pr_err_ratelimited("%s: %s: ignoring interrupt: 0x%08x due to DATATOUT_FOR_R1B quirk\n",
+ mmc_hostname(host->mmc),
+ __func__, intmask);
+ MMC_TRACE(host->mmc,
+ "%s: Quirk ignoring intr: 0x%08x\n",
+ __func__, intmask);
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..9ce0ad854caa
--- /dev/null
+++ b/drivers/net/can/spi/k61.c
@@ -0,0 +1,936 @@
+/* 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;
+ int bits_per_word;
+ int reset_delay_msec;
+};
+
+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 = priv_data->bits_per_word;
+
+ 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");
+
+ err = of_property_read_u32(spi->dev.of_node, "bits-per-word",
+ &priv_data->bits_per_word);
+ if (err)
+ priv_data->bits_per_word = 16;
+
+ err = of_property_read_u32(spi->dev.of_node, "reset-delay-msec",
+ &priv_data->reset_delay_msec);
+ if (err)
+ priv_data->reset_delay_msec = 1;
+
+ priv_data->reset = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0);
+ if (gpio_is_valid(priv_data->reset)) {
+ 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);
+ goto cleanup_candev;
+ }
+
+ gpio_direction_output(priv_data->reset, 0);
+ udelay(1);
+ gpio_direction_output(priv_data->reset, 1);
+ msleep(priv_data->reset_delay_msec);
+ }
+
+ 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" },
+ { .compatible = "nxp,mpc5746c" },
+ { }
+};
+
+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/can/spi/rh850.c b/drivers/net/can/spi/rh850.c
index a93f979da9ed..d2b6e8caa112 100644
--- a/drivers/net/can/spi/rh850.c
+++ b/drivers/net/can/spi/rh850.c
@@ -1020,6 +1020,8 @@ static int rh850_create_netdev(struct spi_device *spi,
return -ENOMEM;
}
+ netdev->mtu = CANFD_MTU;
+
netdev_priv_data = netdev_priv(netdev);
netdev_priv_data->rh850_can = priv_data;
netdev_priv_data->netdev_index = index;
diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c
index ecc4a334c507..0a54e7dac0ab 100644
--- a/drivers/net/ethernet/aurora/nb8800.c
+++ b/drivers/net/ethernet/aurora/nb8800.c
@@ -608,7 +608,7 @@ static void nb8800_mac_config(struct net_device *dev)
mac_mode |= HALF_DUPLEX;
if (gigabit) {
- if (priv->phy_mode == PHY_INTERFACE_MODE_RGMII)
+ if (phy_interface_is_rgmii(dev->phydev))
mac_mode |= RGMII_MODE;
mac_mode |= GMAC_MODE;
@@ -1295,11 +1295,10 @@ static int nb8800_tangox_init(struct net_device *dev)
break;
case PHY_INTERFACE_MODE_RGMII:
- pad_mode = PAD_MODE_RGMII;
- break;
-
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
- pad_mode = PAD_MODE_RGMII | PAD_MODE_GTX_CLK_DELAY;
+ pad_mode = PAD_MODE_RGMII;
break;
default:
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index 70da30095b89..a5e4b4b93d1b 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -1583,6 +1583,11 @@ static int bgmac_probe(struct bcma_device *core)
dev_warn(&core->dev, "Using random MAC: %pM\n", mac);
}
+ /* This (reset &) enable is not preset in specs or reference driver but
+ * Broadcom does it in arch PCI code when enabling fake PCI device.
+ */
+ bcma_core_enable(core, 0);
+
/* Allocation and references */
net_dev = alloc_etherdev(sizeof(*bgmac));
if (!net_dev)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 21e5b9ed1ead..3613469dc5c6 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -8722,11 +8722,14 @@ static void tg3_free_consistent(struct tg3 *tp)
tg3_mem_rx_release(tp);
tg3_mem_tx_release(tp);
+ /* Protect tg3_get_stats64() from reading freed tp->hw_stats. */
+ tg3_full_lock(tp, 0);
if (tp->hw_stats) {
dma_free_coherent(&tp->pdev->dev, sizeof(struct tg3_hw_stats),
tp->hw_stats, tp->stats_mapping);
tp->hw_stats = NULL;
}
+ tg3_full_unlock(tp);
}
/*
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c
index 2a9dd460a95f..e1f9e7cebf8f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.c
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.c
@@ -118,8 +118,13 @@ static int mlx4_alloc_icm_coherent(struct device *dev, struct scatterlist *mem,
if (!buf)
return -ENOMEM;
+ if (offset_in_page(buf)) {
+ dma_free_coherent(dev, PAGE_SIZE << order,
+ buf, sg_dma_address(mem));
+ return -ENOMEM;
+ }
+
sg_set_buf(mem, buf, PAGE_SIZE << order);
- BUG_ON(mem->offset);
sg_dma_len(mem) = PAGE_SIZE << order;
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index cc199063612a..6c66d2979795 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -630,6 +630,10 @@ static void dump_command(struct mlx5_core_dev *dev,
pr_debug("\n");
}
+static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg);
+static void mlx5_free_cmd_msg(struct mlx5_core_dev *dev,
+ struct mlx5_cmd_msg *msg);
+
static void cmd_work_handler(struct work_struct *work)
{
struct mlx5_cmd_work_ent *ent = container_of(work, struct mlx5_cmd_work_ent, work);
@@ -638,16 +642,27 @@ static void cmd_work_handler(struct work_struct *work)
struct mlx5_cmd_layout *lay;
struct semaphore *sem;
unsigned long flags;
+ int alloc_ret;
sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem;
down(sem);
if (!ent->page_queue) {
- ent->idx = alloc_ent(cmd);
- if (ent->idx < 0) {
+ alloc_ret = alloc_ent(cmd);
+ if (alloc_ret < 0) {
+ if (ent->callback) {
+ ent->callback(-EAGAIN, ent->context);
+ mlx5_free_cmd_msg(dev, ent->out);
+ free_msg(dev, ent->in);
+ free_cmd(ent);
+ } else {
+ ent->ret = -EAGAIN;
+ complete(&ent->done);
+ }
mlx5_core_err(dev, "failed to allocate command entry\n");
up(sem);
return;
}
+ ent->idx = alloc_ret;
} else {
ent->idx = cmd->max_reg_cmds;
spin_lock_irqsave(&cmd->alloc_lock, flags);
diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c
index 60b7a64c2edb..de14dcc6f4ed 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c
@@ -942,10 +942,13 @@ net_dev_reg_fail:
netif_napi_del(&(rmnet_mhi_ptr->napi));
free_netdev(rmnet_mhi_ptr->dev);
net_dev_alloc_fail:
- mhi_close_channel(rmnet_mhi_ptr->rx_client_handle);
- rmnet_mhi_ptr->dev = NULL;
+ if (rmnet_mhi_ptr->rx_client_handle) {
+ mhi_close_channel(rmnet_mhi_ptr->rx_client_handle);
+ rmnet_mhi_ptr->dev = NULL;
+ }
mhi_rx_chan_start_fail:
- mhi_close_channel(rmnet_mhi_ptr->tx_client_handle);
+ if (rmnet_mhi_ptr->tx_client_handle)
+ mhi_close_channel(rmnet_mhi_ptr->tx_client_handle);
mhi_tx_chan_start_fail:
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited ret %d.\n", ret);
return ret;
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 79ef799f88ab..c5ea1018cb47 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -326,6 +326,7 @@ enum cfg_version {
static const struct pci_device_id rtl8169_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8129), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8136), 0, 0, RTL_CFG_2 },
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8161), 0, 0, RTL_CFG_1 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), 0, 0, RTL_CFG_1 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), 0, 0, RTL_CFG_0 },
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 480f3dae0780..479af106aaeb 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -750,6 +750,7 @@ static struct sh_eth_cpu_data sh7734_data = {
.tsu = 1,
.hw_crc = 1,
.select_mii = 1,
+ .shift_rd0 = 1,
};
/* SH7763 */
@@ -818,6 +819,7 @@ static struct sh_eth_cpu_data r8a7740_data = {
.rpadir_value = 2 << 16,
.no_trimd = 1,
.no_ade = 1,
+ .hw_crc = 1,
.tsu = 1,
.select_mii = 1,
.shift_rd0 = 1,
diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c
index bca6a1e72d1d..e1bb802d4a4d 100644
--- a/drivers/net/irda/mcs7780.c
+++ b/drivers/net/irda/mcs7780.c
@@ -141,9 +141,19 @@ static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val)
static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val)
{
struct usb_device *dev = mcs->usbdev;
- int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
- MCS_RD_RTYPE, 0, reg, val, 2,
- msecs_to_jiffies(MCS_CTRL_TIMEOUT));
+ void *dmabuf;
+ int ret;
+
+ dmabuf = kmalloc(sizeof(__u16), GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+ MCS_RD_RTYPE, 0, reg, dmabuf, 2,
+ msecs_to_jiffies(MCS_CTRL_TIMEOUT));
+
+ memcpy(val, dmabuf, sizeof(__u16));
+ kfree(dmabuf);
return ret;
}
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 84b9cca152eb..e83acc608678 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -907,7 +907,7 @@ static void decode_txts(struct dp83640_private *dp83640,
if (overflow) {
pr_debug("tx timestamp queue overflow, count %d\n", overflow);
while (skb) {
- skb_complete_tx_timestamp(skb, NULL);
+ kfree_skb(skb);
skb = skb_dequeue(&dp83640->tx_queue);
}
return;
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index 32f10662f4ac..7242dd4b3238 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -29,6 +29,7 @@
#define MII_DP83867_MICR 0x12
#define MII_DP83867_ISR 0x13
#define DP83867_CTRL 0x1f
+#define DP83867_CFG3 0x1e
/* Extended Registers */
#define DP83867_RGMIICTL 0x0032
@@ -89,6 +90,8 @@ static int dp83867_config_intr(struct phy_device *phydev)
micr_status |=
(MII_DP83867_MICR_AN_ERR_INT_EN |
MII_DP83867_MICR_SPEED_CHNG_INT_EN |
+ MII_DP83867_MICR_AUTONEG_COMP_INT_EN |
+ MII_DP83867_MICR_LINK_STS_CHNG_INT_EN |
MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN |
MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN);
@@ -184,6 +187,13 @@ static int dp83867_config_init(struct phy_device *phydev)
DP83867_DEVADDR, phydev->addr, delay);
}
+ /* Enable Interrupt output INT_OE in CFG3 register */
+ if (phy_interrupt_is_valid(phydev)) {
+ val = phy_read(phydev, DP83867_CFG3);
+ val |= BIT(7);
+ phy_write(phydev, DP83867_CFG3, val);
+ }
+
return 0;
}
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index e13ad6cdcc22..c8b85f1069ff 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -539,6 +539,8 @@ static int ksz9031_read_status(struct phy_device *phydev)
if ((regval & 0xFF) == 0xFF) {
phy_init_hw(phydev);
phydev->link = 0;
+ if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev))
+ phydev->drv->config_intr(phydev);
}
return 0;
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 851c0e121807..49d9f0a789fe 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -541,6 +541,9 @@ void phy_stop_machine(struct phy_device *phydev)
if (phydev->state > PHY_UP && phydev->state != PHY_HALTED)
phydev->state = PHY_UP;
mutex_unlock(&phydev->lock);
+
+ /* Now we can run the state machine synchronously */
+ phy_state_machine(&phydev->state_queue.work);
}
/**
@@ -918,6 +921,15 @@ void phy_state_machine(struct work_struct *work)
if (old_link != phydev->link)
phydev->state = PHY_CHANGELINK;
}
+ /*
+ * Failsafe: check that nobody set phydev->link=0 between two
+ * poll cycles, otherwise we won't leave RUNNING state as long
+ * as link remains down.
+ */
+ if (!phydev->link && phydev->state == PHY_RUNNING) {
+ phydev->state = PHY_CHANGELINK;
+ dev_err(&phydev->dev, "no link in PHY_RUNNING\n");
+ }
break;
case PHY_CHANGELINK:
err = phy_read_status(phydev);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 0bfbabad4431..8179727d3423 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1368,6 +1368,8 @@ static int phy_remove(struct device *dev)
{
struct phy_device *phydev = to_phy_device(dev);
+ cancel_delayed_work_sync(&phydev->state_queue);
+
mutex_lock(&phydev->lock);
phydev->state = PHY_DOWN;
mutex_unlock(&phydev->lock);
@@ -1442,7 +1444,7 @@ static struct phy_driver genphy_driver[] = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
- .soft_reset = genphy_soft_reset,
+ .soft_reset = genphy_no_soft_reset,
.config_init = genphy_config_init,
.features = PHY_GBIT_FEATURES | SUPPORTED_MII |
SUPPORTED_AUI | SUPPORTED_FIBRE |
diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c
index f64b25c221e8..cd93220c9b45 100644
--- a/drivers/net/usb/kaweth.c
+++ b/drivers/net/usb/kaweth.c
@@ -1009,6 +1009,7 @@ static int kaweth_probe(
struct net_device *netdev;
const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
int result = 0;
+ int rv = -EIO;
dev_dbg(dev,
"Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x\n",
@@ -1029,6 +1030,7 @@ static int kaweth_probe(
kaweth = netdev_priv(netdev);
kaweth->dev = udev;
kaweth->net = netdev;
+ kaweth->intf = intf;
spin_lock_init(&kaweth->device_lock);
init_waitqueue_head(&kaweth->term_wait);
@@ -1048,6 +1050,10 @@ static int kaweth_probe(
/* Download the firmware */
dev_info(dev, "Downloading firmware...\n");
kaweth->firmware_buf = (__u8 *)__get_free_page(GFP_KERNEL);
+ if (!kaweth->firmware_buf) {
+ rv = -ENOMEM;
+ goto err_free_netdev;
+ }
if ((result = kaweth_download_firmware(kaweth,
"kaweth/new_code.bin",
100,
@@ -1139,8 +1145,6 @@ err_fw:
dev_dbg(dev, "Initializing net device.\n");
- kaweth->intf = intf;
-
kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!kaweth->tx_urb)
goto err_free_netdev;
@@ -1204,7 +1208,7 @@ err_only_tx:
err_free_netdev:
free_netdev(netdev);
- return -EIO;
+ return rv;
}
/****************************************************************
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 582d8f0c6266..958af3b1af7f 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -707,6 +707,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */
{QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
{QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */
+ {QMI_FIXED_INTF(0x2001, 0x7e35, 4)}, /* D-Link DWM-222 */
{QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */
{QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */
{QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 349aecbc210a..ac945f8781ac 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -733,15 +733,15 @@ static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
static void vrf_dev_uninit(struct net_device *dev)
{
struct net_vrf *vrf = netdev_priv(dev);
- struct slave_queue *queue = &vrf->queue;
- struct list_head *head = &queue->all_slaves;
- struct slave *slave, *next;
+// struct slave_queue *queue = &vrf->queue;
+// struct list_head *head = &queue->all_slaves;
+// struct slave *slave, *next;
vrf_rtable_destroy(vrf);
vrf_rt6_destroy(vrf);
- list_for_each_entry_safe(slave, next, head, list)
- vrf_del_slave(dev, slave->dev);
+// list_for_each_entry_safe(slave, next, head, list)
+// vrf_del_slave(dev, slave->dev);
free_percpu(dev->dstats);
dev->dstats = NULL;
@@ -914,6 +914,14 @@ static int vrf_validate(struct nlattr *tb[], struct nlattr *data[])
static void vrf_dellink(struct net_device *dev, struct list_head *head)
{
+ struct net_vrf *vrf = netdev_priv(dev);
+ struct slave_queue *queue = &vrf->queue;
+ struct list_head *all_slaves = &queue->all_slaves;
+ struct slave *slave, *next;
+
+ list_for_each_entry_safe(slave, next, all_slaves, list)
+ vrf_del_slave(dev, slave->dev);
+
unregister_netdevice_queue(dev, head);
}
diff --git a/drivers/net/wireless/ath/ath10k/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/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 6906bddb229f..041d1d5eb718 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1886,6 +1886,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
goto err_wmi_detach;
}
+ /* If firmware indicates Full Rx Reorder support it must be used in a
+ * slightly different manner. Let HTT code know.
+ */
+ ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
+ ar->wmi.svc_map));
+
status = ath10k_htt_rx_alloc(&ar->htt);
if (status) {
ath10k_err(ar, "failed to alloc htt rx: %d\n", status);
@@ -1918,6 +1924,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
goto err_hif_stop;
}
+ status = ath10k_pktlog_connect(ar);
+ if (status) {
+ ath10k_err(ar, "could not connect pktlog: %d\n", status);
+ goto err_hif_stop;
+ }
+
status = ath10k_htc_start(&ar->htc);
if (status) {
ath10k_err(ar, "failed to start htc: %d\n", status);
@@ -1997,12 +2009,6 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
}
}
- /* If firmware indicates Full Rx Reorder support it must be used in a
- * slightly different manner. Let HTT code know.
- */
- ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
- ar->wmi.svc_map));
-
status = ath10k_htt_rx_ring_refill(ar);
if (status) {
ath10k_err(ar, "failed to refill htt rx ring: %d\n", status);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 8ddbae96794d..ffcf30756b9e 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -467,6 +467,7 @@ struct ath10k_debug {
u64 fw_dbglog_mask;
u32 fw_dbglog_level;
u32 pktlog_filter;
+ enum ath10k_htc_ep_id eid;
u32 reg_addr;
u32 nf_cal_period;
void *cal_data;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index ec8063e7986a..42aab9b86af3 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -25,6 +25,7 @@
#include "core.h"
#include "debug.h"
#include "hif.h"
+#include "htt.h"
#include "wmi-ops.h"
/* ms */
@@ -2617,6 +2618,178 @@ static const struct file_operations fops_fw_checksums = {
.llseek = default_llseek,
};
+static struct txctl_frm_hdr frm_hdr;
+
+static void ath10k_extract_frame_header(u8 *addr1, u8 *addr2, u8 *addr3)
+{
+ frm_hdr.bssid_tail = (addr1[IEEE80211_ADDR_LEN - 2] << BITS_PER_BYTE)
+ | (addr1[IEEE80211_ADDR_LEN - 1]);
+ frm_hdr.sa_tail = (addr2[IEEE80211_ADDR_LEN - 2] << BITS_PER_BYTE)
+ | (addr2[IEEE80211_ADDR_LEN - 1]);
+ frm_hdr.da_tail = (addr3[IEEE80211_ADDR_LEN - 2] << BITS_PER_BYTE)
+ | (addr3[IEEE80211_ADDR_LEN - 1]);
+}
+
+static void ath10k_process_ieee_hdr(void *data)
+{
+ u8 dir;
+ struct ieee80211_frame *wh;
+
+ if (!data)
+ return;
+
+ wh = (struct ieee80211_frame *)(data);
+ frm_hdr.framectrl = *(u_int16_t *)(wh->i_fc);
+ frm_hdr.seqctrl = *(u_int16_t *)(wh->i_seq);
+ dir = (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK);
+
+ if (dir == IEEE80211_FC1_DIR_TODS)
+ ath10k_extract_frame_header(wh->i_addr1, wh->i_addr2,
+ wh->i_addr3);
+ else if (dir == IEEE80211_FC1_DIR_FROMDS)
+ ath10k_extract_frame_header(wh->i_addr2, wh->i_addr3,
+ wh->i_addr1);
+ else
+ ath10k_extract_frame_header(wh->i_addr3, wh->i_addr2,
+ wh->i_addr1);
+}
+
+static void ath10k_pktlog_process_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+ struct ath10k_pktlog_hdr *hdr = (void *)skb->data;
+ struct ath_pktlog_txctl pktlog_tx_ctrl;
+
+ switch (hdr->log_type) {
+ case ATH10K_PKTLOG_TYPE_TX_CTRL: {
+ spin_lock_bh(&ar->htt.tx_lock);
+
+ memcpy((void *)(&pktlog_tx_ctrl.hdr), (void *)hdr,
+ sizeof(pktlog_tx_ctrl.hdr));
+ pktlog_tx_ctrl.frm_hdr = frm_hdr;
+ memcpy((void *)pktlog_tx_ctrl.txdesc_ctl, (void *)hdr->payload,
+ __le16_to_cpu(hdr->size));
+ pktlog_tx_ctrl.hdr.size = sizeof(pktlog_tx_ctrl) -
+ sizeof(pktlog_tx_ctrl.hdr);
+
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+ trace_ath10k_htt_pktlog(ar, (void *)&pktlog_tx_ctrl,
+ sizeof(pktlog_tx_ctrl));
+ break;
+ }
+ case ATH10K_PKTLOG_TYPE_TX_MSDU_ID:
+ break;
+ case ATH10K_PKTLOG_TYPE_TX_FRM_HDR: {
+ ath10k_process_ieee_hdr((void *)(hdr->payload));
+ trace_ath10k_htt_pktlog(ar, hdr, sizeof(*hdr) +
+ __le16_to_cpu(hdr->size));
+ break;
+ }
+ case ATH10K_PKTLOG_TYPE_RX_STAT:
+ case ATH10K_PKTLOG_TYPE_RC_FIND:
+ case ATH10K_PKTLOG_TYPE_RC_UPDATE:
+ case ATH10K_PKTLOG_TYPE_DBG_PRINT:
+ case ATH10K_PKTLOG_TYPE_TX_STAT:
+ case ATH10K_PKTLOG_TYPE_SW_EVENT:
+ trace_ath10k_htt_pktlog(ar, hdr, sizeof(*hdr) +
+ __le16_to_cpu(hdr->size));
+ break;
+ case ATH10K_PKTLOG_TYPE_TX_VIRT_ADDR: {
+ u32 desc_id = (u32)*((u32 *)(hdr->payload));
+ struct sk_buff *msdu;
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ msdu = ath10k_htt_tx_find_msdu_by_id(&ar->htt, desc_id);
+
+ if (!msdu) {
+ ath10k_info(ar,
+ "Failed to get msdu, id: %d\n",
+ desc_id);
+ spin_unlock_bh(&ar->htt.tx_lock);
+ return;
+ }
+ ath10k_process_ieee_hdr((void *)msdu->data);
+ spin_unlock_bh(&ar->htt.tx_lock);
+ trace_ath10k_htt_pktlog(ar, hdr, sizeof(*hdr) +
+ __le16_to_cpu(hdr->size));
+ break;
+ }
+ }
+}
+
+int ath10k_rx_record_pktlog(struct ath10k *ar, struct sk_buff *skb)
+{
+ struct sk_buff *pktlog_skb;
+ struct ath_pktlog_hdr *pl_hdr;
+ struct ath_pktlog_rx_info *pktlog_rx_info;
+ struct htt_rx_desc *rx_desc = (void *)skb->data - sizeof(*rx_desc);
+
+ if (!ar->debug.pktlog_filter)
+ return 0;
+
+ pktlog_skb = dev_alloc_skb(sizeof(struct ath_pktlog_hdr) +
+ sizeof(struct htt_rx_desc) -
+ sizeof(struct htt_host_fw_desc_base));
+ if (!pktlog_skb)
+ return -ENOMEM;
+
+ pktlog_rx_info = (struct ath_pktlog_rx_info *)pktlog_skb->data;
+ pl_hdr = &pktlog_rx_info->pl_hdr;
+
+ pl_hdr->flags = (1 << ATH10K_PKTLOG_FLG_FRM_TYPE_REMOTE_S);
+ pl_hdr->missed_cnt = 0;
+ pl_hdr->mac_id = 0;
+ pl_hdr->log_type = ATH10K_PKTLOG_TYPE_RX_STAT;
+ pl_hdr->flags |= ATH10K_PKTLOG_HDR_SIZE_16;
+ pl_hdr->size = sizeof(*rx_desc) -
+ sizeof(struct htt_host_fw_desc_base);
+
+ pl_hdr->timestamp =
+ cpu_to_le32(rx_desc->ppdu_end.wcn3990.rx_pkt_end.phy_timestamp_1);
+
+ pl_hdr->type_specific_data = 0xDEADAA;
+ memcpy((void *)pktlog_rx_info + sizeof(struct ath_pktlog_hdr),
+ (void *)rx_desc + sizeof(struct htt_host_fw_desc_base),
+ pl_hdr->size);
+
+ ath10k_pktlog_process_rx(ar, pktlog_skb);
+ dev_kfree_skb_any(pktlog_skb);
+ return 0;
+}
+
+static void ath10k_pktlog_htc_tx_complete(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ ath10k_info(ar, "PKTLOG htc completed\n");
+}
+
+int ath10k_pktlog_connect(struct ath10k *ar)
+{
+ int status;
+ struct ath10k_htc_svc_conn_req conn_req;
+ struct ath10k_htc_svc_conn_resp conn_resp;
+
+ memset(&conn_req, 0, sizeof(conn_req));
+ memset(&conn_resp, 0, sizeof(conn_resp));
+
+ conn_req.ep_ops.ep_tx_complete = ath10k_pktlog_htc_tx_complete;
+ conn_req.ep_ops.ep_rx_complete = ath10k_pktlog_process_rx;
+ conn_req.ep_ops.ep_tx_credits = NULL;
+
+ /* connect to control service */
+ conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_LOG_MSG;
+ status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp);
+ if (status) {
+ ath10k_warn(ar, "failed to connect to PKTLOG service: %d\n",
+ status);
+ return status;
+ }
+
+ ar->debug.eid = conn_resp.eid;
+
+ return 0;
+}
+
int ath10k_debug_create(struct ath10k *ar)
{
ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data));
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index f963391e3544..eb47720bbf91 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -57,6 +57,84 @@ enum ath10k_dbg_aggr_mode {
ATH10K_DBG_AGGR_MODE_MAX,
};
+#define IEEE80211_FC1_DIR_MASK 0x03
+#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */
+#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */
+#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */
+#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */
+#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */
+
+#define MAX_PKT_INFO_MSDU_ID 192
+#define MSDU_ID_INFO_ID_OFFSET \
+ ((MAX_PKT_INFO_MSDU_ID >> 3) + 4)
+
+#define PKTLOG_MAX_TXCTL_WORDS 57 /* +2 words for bitmap */
+#define HTT_TX_MSDU_LEN_MASK 0xffff
+
+struct txctl_frm_hdr {
+ __le16 framectrl; /* frame control field from header */
+ __le16 seqctrl; /* frame control field from header */
+ __le16 bssid_tail; /* last two octets of bssid */
+ __le16 sa_tail; /* last two octets of SA */
+ __le16 da_tail; /* last two octets of DA */
+ __le16 resvd;
+} __packed;
+
+struct ath_pktlog_hdr {
+ __le16 flags;
+ __le16 missed_cnt;
+ u8 log_type;
+ u8 mac_id;
+ __le16 size;
+ __le32 timestamp;
+ __le32 type_specific_data;
+} __packed;
+
+/* generic definitions for IEEE 802.11 frames */
+struct ieee80211_frame {
+ u8 i_fc[2];
+ u8 i_dur[2];
+ union {
+ struct {
+ u8 i_addr1[IEEE80211_ADDR_LEN];
+ u8 i_addr2[IEEE80211_ADDR_LEN];
+ u8 i_addr3[IEEE80211_ADDR_LEN];
+ };
+ u8 i_addr_all[3 * IEEE80211_ADDR_LEN];
+ };
+ u8 i_seq[2];
+} __packed;
+
+struct fw_pktlog_msdu_info {
+ __le32 num_msdu;
+ u8 bound_bmap[MAX_PKT_INFO_MSDU_ID >> 3];
+ __le16 id[MAX_PKT_INFO_MSDU_ID];
+} __packed;
+
+struct ath_pktlog_txctl {
+ struct ath_pktlog_hdr hdr;
+ struct txctl_frm_hdr frm_hdr;
+ __le32 txdesc_ctl[PKTLOG_MAX_TXCTL_WORDS];
+} __packed;
+
+struct ath_pktlog_msdu_id {
+ struct ath_pktlog_hdr hdr;
+ struct fw_pktlog_msdu_info msdu_info;
+} __packed;
+
+struct ath_pktlog_rx_info {
+ struct ath_pktlog_hdr pl_hdr;
+ struct rx_attention attention;
+ struct rx_frag_info frag_info;
+ struct rx_mpdu_start mpdu_start;
+ struct rx_msdu_start msdu_start;
+ struct rx_msdu_end msdu_end;
+ struct rx_mpdu_end mpdu_end;
+ struct rx_ppdu_start ppdu_start;
+ struct rx_ppdu_end ppdu_end;
+ u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN];
+} __packed;
+
/* FIXME: How to calculate the buffer size sanely? */
#define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024)
#define ATH10K_DATAPATH_BUF_SIZE (1024 * 1024)
@@ -86,6 +164,7 @@ struct ath10k_fw_crash_data *
ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len);
+int ath10k_rx_record_pktlog(struct ath10k *ar, struct sk_buff *skb);
#define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
@@ -98,6 +177,7 @@ void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
struct ethtool_stats *stats, u64 *data);
void fill_datapath_stats(struct ath10k *ar, struct ieee80211_rx_status *status);
size_t get_datapath_stat(char *buf, struct ath10k *ar);
+int ath10k_pktlog_connect(struct ath10k *ar);
#else
static inline int ath10k_debug_start(struct ath10k *ar)
{
@@ -142,6 +222,12 @@ static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer,
{
}
+static inline int ath10k_rx_record_pktlog(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ return 0;
+}
+
static inline struct ath10k_fw_crash_data *
ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
{
@@ -158,6 +244,10 @@ static inline size_t get_datapath_stat(char *buf, struct ath10k *ar)
return 0;
}
+static inline int ath10k_pktlog_connect(struct ath10k *ar)
+{
+ return 0;
+}
#define ATH10K_DFS_STAT_INC(ar, c) do { } while (0)
#define ath10k_debug_get_et_strings NULL
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index b0d6c2614731..6377c4ef427c 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1745,16 +1745,21 @@ struct ath10k_htt {
#define RX_HTT_HDR_STATUS_LEN 64
-/* This structure layout is programmed via rx ring setup
- * so that FW knows how to transfer the rx descriptor to the host.
- * Buffers like this are placed on the rx ring. */
-struct htt_rx_desc {
+struct htt_host_fw_desc_base {
union {
/* This field is filled on the host using the msdu buffer
* from htt_rx_indication */
struct fw_rx_desc_base fw_desc;
u32 pad;
} __packed;
+};
+
+/* This structure layout is programmed via rx ring setup
+ * so that FW knows how to transfer the rx descriptor to the host.
+ * Buffers like this are placed on the rx ring.
+ */
+struct htt_rx_desc {
+ struct htt_host_fw_desc_base fw_desc_base;
struct {
struct rx_attention attention;
struct rx_frag_info frag_info;
@@ -1847,5 +1852,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt,
void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
struct sk_buff *skb);
int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget);
+struct sk_buff *ath10k_htt_tx_find_msdu_by_id(struct ath10k_htt *htt,
+ u16 msdu_id);
#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 437ea2c192b3..635b8281b055 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -969,6 +969,7 @@ static void ath10k_process_rx(struct ath10k *ar,
trace_ath10k_rx_hdr(ar, skb->data, skb->len);
trace_ath10k_rx_payload(ar, skb->data, skb->len);
+ ath10k_rx_record_pktlog(ar, skb);
ieee80211_rx_napi(ar->hw, NULL, skb, &ar->napi);
}
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 1b59721a91ac..6579eb2b410c 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -218,6 +218,27 @@ int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb)
return ret;
}
+struct sk_buff *ath10k_htt_tx_find_msdu_by_id(struct ath10k_htt *htt,
+ u16 msdu_id)
+{
+ struct ath10k *ar;
+ struct sk_buff *ret;
+
+ if (!htt)
+ return NULL;
+
+ ar = htt->ar;
+
+ lockdep_assert_held(&htt->tx_lock);
+
+ ret = (struct sk_buff *)idr_find(&htt->pending_tx, msdu_id);
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx find msdu by msdu_id %s\n",
+ !ret ? "Failed" : "Success");
+
+ return ret;
+}
+
void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
{
struct ath10k *ar = htt->ar;
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 0f2422480c4e..37479589b8e1 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -984,4 +984,37 @@ struct ath10k_shadow_reg_address {
extern struct ath10k_shadow_reg_value wcn3990_shadow_reg_value;
extern struct ath10k_shadow_reg_address wcn3990_shadow_reg_address;
+#define ATH10K_PKTLOG_HDR_SIZE_16 0x8000
+
+enum {
+ ATH10k_PKTLOG_FLG_FRM_TYPE_LOCAL_S = 0,
+ ATH10K_PKTLOG_FLG_FRM_TYPE_REMOTE_S,
+ ATH10K_PKTLOG_FLG_FRM_TYPE_CLONE_S,
+ ATH10K_PKTLOG_FLG_FRM_TYPE_CBF_S,
+ ATH10K_PKTLOG_FLG_FRM_TYPE_UNKNOWN_S
+};
+
+enum ath10k_pktlog_type {
+ ATH10K_PKTLOG_TYPE_TX_CTRL = 1,
+ ATH10K_PKTLOG_TYPE_TX_STAT,
+ ATH10K_PKTLOG_TYPE_TX_MSDU_ID,
+ ATH10K_PKTLOG_TYPE_TX_FRM_HDR,
+ ATH10K_PKTLOG_TYPE_RX_STAT,
+ ATH10K_PKTLOG_TYPE_RC_FIND,
+ ATH10K_PKTLOG_TYPE_RC_UPDATE,
+ ATH10K_PKTLOG_TYPE_TX_VIRT_ADDR,
+ ATH10K_PKTLOG_TYPE_DBG_PRINT,
+ ATH10K_PKTLOG_TYPE_SW_EVENT,
+ ATH10K_PKTLOG_TYPE_MAX,
+};
+
+struct ath10k_pktlog_hdr {
+ __le16 flags;
+ __le16 missed_cnt;
+ __le16 log_type;
+ __le16 size;
+ __le32 timestamp;
+ u8 payload[0];
+} __packed;
+
#endif /* _HW_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index cce931e51d7e..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) {
@@ -7822,10 +7829,6 @@ static const struct ieee80211_iface_limit ath10k_wcn3990_if_limit[] = {
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO),
},
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
- },
};
static const struct ieee80211_iface_limit ath10k_wcn3990_qcs_if_limit[] = {
@@ -7845,10 +7848,6 @@ static const struct ieee80211_iface_limit ath10k_wcn3990_qcs_if_limit[] = {
#endif
BIT(NL80211_IFTYPE_P2P_GO),
},
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
- },
};
static const struct ieee80211_iface_limit ath10k_wcn3990_if_limit_ibss[] = {
@@ -8022,6 +8021,10 @@ int ath10k_mac_register(struct ath10k *ar)
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO);
+ if (QCA_REV_WCN3990(ar))
+ ar->hw->wiphy->interface_modes &=
+ ~BIT(NL80211_IFTYPE_P2P_DEVICE);
+
ieee80211_hw_set(ar->hw, SIGNAL_DBM);
ieee80211_hw_set(ar->hw, SUPPORTS_PS);
ieee80211_hw_set(ar->hw, SUPPORTS_DYNAMIC_PS);
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 9e607b2fa2d4..ca77861c4320 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -3139,7 +3139,7 @@ int ath10k_pci_setup_resource(struct ath10k *ar)
setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry,
(unsigned long)ar);
- if (QCA_REV_6174(ar))
+ if (QCA_REV_6174(ar) || QCA_REV_9377(ar))
ath10k_pci_override_ce_config(ar);
ret = ath10k_pci_alloc_pipes(ar);
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
index 10223605b027..dd310d5a7028 100644
--- a/drivers/net/wireless/ath/ath10k/qmi.c
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -719,6 +719,7 @@ static int ath10k_snoc_driver_event_server_exit(struct ath10k *ar)
atomic_set(&qmi_cfg->fw_ready, 0);
qmi_cfg->msa_ready = false;
atomic_set(&qmi_cfg->server_connected, 0);
+ qmi_handle_destroy(qmi_cfg->wlfw_clnt);
return 0;
}
@@ -896,5 +897,6 @@ void ath10k_snoc_stop_qmi_service(struct ath10k *ar)
WLFW_SERVICE_INS_ID_V01,
&qmi_cfg->wlfw_clnt_nb);
destroy_workqueue(qmi_cfg->event_wq);
+ qmi_handle_destroy(qmi_cfg->wlfw_clnt);
qmi_cfg = NULL;
}
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index 9406b6f71a6b..13736750e463 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -107,8 +107,8 @@ static struct ce_attr host_ce_config_wlan[] = {
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 0,
- .src_sz_max = 512,
- .dest_nentries = 512,
+ .src_sz_max = 0,
+ .dest_nentries = 0,
.recv_cb = ath10k_snoc_htt_rx_cb,
},
@@ -775,9 +775,6 @@ static int ath10k_snoc_hif_map_service_to_pipe(struct ath10k *ar,
}
}
- if (WARN_ON(!ul_set || !dl_set))
- return -ENOENT;
-
return 0;
}
@@ -1274,19 +1271,19 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
ret = ath10k_snoc_claim(ar);
if (ret) {
ath10k_err(ar, "failed to claim device: %d\n", ret);
- goto err_core_destroy;
+ goto err_stop_qmi_service;
}
ret = ath10k_snoc_bus_configure(ar);
if (ret) {
ath10k_err(ar, "failed to configure bus: %d\n", ret);
- goto err_core_destroy;
+ goto err_stop_qmi_service;
}
ret = ath10k_snoc_alloc_pipes(ar);
if (ret) {
ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
ret);
- goto err_core_destroy;
+ goto err_stop_qmi_service;
}
netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll,
@@ -1319,6 +1316,9 @@ err_free_irq:
err_free_pipes:
ath10k_snoc_free_pipes(ar);
+err_stop_qmi_service:
+ ath10k_snoc_stop_qmi_service(ar);
+
err_core_destroy:
ath10k_core_destroy(ar);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
index c3100fcd80f2..8bb01c1a35f7 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
@@ -681,6 +681,9 @@ ath10k_wmi_vdev_spectral_conf(struct ath10k *ar,
struct sk_buff *skb;
u32 cmd_id;
+ if (!ar->wmi.ops->gen_vdev_spectral_conf)
+ return -EOPNOTSUPP;
+
skb = ar->wmi.ops->gen_vdev_spectral_conf(ar, arg);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -696,6 +699,9 @@ ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger,
struct sk_buff *skb;
u32 cmd_id;
+ if (!ar->wmi.ops->gen_vdev_spectral_enable)
+ return -EOPNOTSUPP;
+
skb = ar->wmi.ops->gen_vdev_spectral_enable(ar, vdev_id, trigger,
enable);
if (IS_ERR(skb))
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 201425e7f9cb..fbc8c9a9014b 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -1815,8 +1815,6 @@ static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah)
static void ar9003_hw_tx99_start(struct ath_hw *ah, u32 qnum)
{
REG_SET_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR);
- REG_SET_BIT(ah, 0x9864, 0x7f000);
- REG_SET_BIT(ah, 0x9924, 0x7f00fe);
REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
REG_WRITE(ah, AR_CR, AR_CR_RXD);
REG_WRITE(ah, AR_DLCL_IFS(qnum), 0);
diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c
index ac4781f37e78..b4e6304afd40 100644
--- a/drivers/net/wireless/ath/ath9k/tx99.c
+++ b/drivers/net/wireless/ath/ath9k/tx99.c
@@ -190,22 +190,27 @@ static ssize_t write_file_tx99(struct file *file, const char __user *user_buf,
if (strtobool(buf, &start))
return -EINVAL;
+ mutex_lock(&sc->mutex);
+
if (start == sc->tx99_state) {
if (!start)
- return count;
+ goto out;
ath_dbg(common, XMIT, "Resetting TX99\n");
ath9k_tx99_deinit(sc);
}
if (!start) {
ath9k_tx99_deinit(sc);
- return count;
+ goto out;
}
r = ath9k_tx99_init(sc);
- if (r)
+ if (r) {
+ mutex_unlock(&sc->mutex);
return r;
-
+ }
+out:
+ mutex_unlock(&sc->mutex);
return count;
}
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
index 0e66348e7513..2ab6c5951561 100644
--- a/drivers/net/wireless/ath/wil6210/Kconfig
+++ b/drivers/net/wireless/ath/wil6210/Kconfig
@@ -60,3 +60,15 @@ config WIL6210_PLATFORM_MSM
---help---
Say Y here to enable wil6210 driver support for MSM
platform specific features
+
+config WIL6210_DEBUGFS
+ bool "wil6210 debugfs support"
+ depends on WIL6210
+ depends on DEBUG_FS
+ default y
+ ---help---
+ Say Y here to enable wil6210 debugfs support, using the
+ kernel debugfs infrastructure. Select this
+ option if you are interested in debugging the driver.
+
+ If unsure, say Y to make it easier to debug problems.
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index 4874c5ba1e61..94df1decae7a 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -4,7 +4,7 @@ wil6210-y := main.o
wil6210-y += netdev.o
wil6210-y += cfg80211.o
wil6210-y += pcie_bus.o
-wil6210-y += debugfs.o
+wil6210-$(CONFIG_WIL6210_DEBUGFS) += debugfs.o
wil6210-y += sysfs.o
wil6210-y += wmi.o
wil6210-y += interrupt.o
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 63bb7686b811..35dfa410c90c 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;
@@ -445,6 +455,34 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy,
return rc;
}
+static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+ wil_dbg_misc(wil, "start_p2p_device: entered\n");
+ wil->p2p.p2p_dev_started = 1;
+ return 0;
+}
+
+static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil_p2p_info *p2p = &wil->p2p;
+
+ if (!p2p->p2p_dev_started)
+ return;
+
+ wil_dbg_misc(wil, "stop_p2p_device: entered\n");
+ mutex_lock(&wil->mutex);
+ mutex_lock(&wil->p2p_wdev_mutex);
+ wil_p2p_stop_radio_operations(wil);
+ p2p->p2p_dev_started = 0;
+ mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->mutex);
+}
+
static struct wireless_dev *
wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
unsigned char name_assign_type,
@@ -493,6 +531,7 @@ static int wil_cfg80211_del_iface(struct wiphy *wiphy,
return -EINVAL;
}
+ wil_cfg80211_stop_p2p_device(wiphy, wdev);
wil_p2p_wdev_free(wil);
return 0;
@@ -877,7 +916,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
wil->bss = bss;
/* Connect can take lots of time */
mod_timer(&wil->connect_timer,
- jiffies + msecs_to_jiffies(2000));
+ jiffies + msecs_to_jiffies(5000));
} else {
clear_bit(wil_status_fwconnecting, wil->status);
}
@@ -960,7 +999,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf,
len, true);
- if (len < sizeof(struct ieee80211_mgmt))
+ if (len < sizeof(struct ieee80211_hdr_3addr))
return -EINVAL;
cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
@@ -1727,34 +1766,6 @@ static int wil_cfg80211_change_bss(struct wiphy *wiphy,
return 0;
}
-static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
- struct wireless_dev *wdev)
-{
- struct wil6210_priv *wil = wiphy_to_wil(wiphy);
-
- wil_dbg_misc(wil, "start_p2p_device: entered\n");
- wil->p2p.p2p_dev_started = 1;
- return 0;
-}
-
-static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
- struct wireless_dev *wdev)
-{
- struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- struct wil_p2p_info *p2p = &wil->p2p;
-
- if (!p2p->p2p_dev_started)
- return;
-
- wil_dbg_misc(wil, "stop_p2p_device: entered\n");
- mutex_lock(&wil->mutex);
- mutex_lock(&wil->p2p_wdev_mutex);
- wil_p2p_stop_radio_operations(wil);
- p2p->p2p_dev_started = 0;
- mutex_unlock(&wil->p2p_wdev_mutex);
- mutex_unlock(&wil->mutex);
-}
-
static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy,
struct net_device *dev,
bool enabled, int timeout)
@@ -1870,7 +1881,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 +1898,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/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index cad8a95c4e4e..59def4f3fcf3 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -244,7 +244,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
if (unlikely(!isr)) {
- wil_err(wil, "spurious IRQ: RX\n");
+ wil_err_ratelimited(wil, "spurious IRQ: RX\n");
return IRQ_NONE;
}
@@ -269,11 +269,12 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
need_unmask = false;
napi_schedule(&wil->napi_rx);
} else {
- wil_err(wil,
+ wil_err_ratelimited(
+ wil,
"Got Rx interrupt while stopping interface\n");
}
} else {
- wil_err(wil, "Got Rx interrupt while in reset\n");
+ wil_err_ratelimited(wil, "Got Rx interrupt while in reset\n");
}
}
@@ -302,7 +303,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
if (unlikely(!isr)) {
- wil_err(wil, "spurious IRQ: TX\n");
+ wil_err_ratelimited(wil, "spurious IRQ: TX\n");
return IRQ_NONE;
}
@@ -318,12 +319,13 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
need_unmask = false;
napi_schedule(&wil->napi_tx);
} else {
- wil_err(wil, "Got Tx interrupt while in reset\n");
+ wil_err_ratelimited(wil, "Got Tx interrupt while in reset\n");
}
}
if (unlikely(isr))
- wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
+ wil_err_ratelimited(wil, "un-handled TX ISR bits 0x%08x\n",
+ isr);
/* Tx IRQ will be enabled when NAPI processing finished */
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 78091b7910c7..e39e3dbb62e9 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;
@@ -927,6 +931,29 @@ int wil_ps_update(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile)
return rc;
}
+static void wil_pre_fw_config(struct wil6210_priv *wil)
+{
+ /* Mark FW as loaded from host */
+ wil_s(wil, RGF_USER_USAGE_6, 1);
+
+ /* clear any interrupts which on-card-firmware
+ * may have set
+ */
+ wil6210_clear_irq(wil);
+ /* CAF_ICR - clear and mask */
+ /* it is W1C, clear by writing back same value */
+ wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
+ wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
+ /* clear PAL_UNIT_ICR (potential D0->D3 leftover) */
+ wil_s(wil, RGF_PAL_UNIT_ICR + offsetof(struct RGF_ICR, ICR), 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);
+ }
+}
+
/*
* We reset all the structures, and we reset the UMAC.
* After calling this routine, you're expected to reload
@@ -1024,18 +1051,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
if (rc)
return rc;
- /* Mark FW as loaded from host */
- wil_s(wil, RGF_USER_USAGE_6, 1);
-
- /* clear any interrupts which on-card-firmware
- * may have set
- */
- wil6210_clear_irq(wil);
- /* CAF_ICR - clear and mask */
- /* it is W1C, clear by writing back same value */
- wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
- wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
-
+ wil_pre_fw_config(wil);
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..673a953fcf6e 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" */
@@ -267,6 +275,7 @@ struct RGF_ICR {
#define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2)
#define RGF_HP_CTRL (0x88265c)
+#define RGF_PAL_UNIT_ICR (0x88266c) /* struct RGF_ICR */
#define RGF_PCIE_LOS_COUNTER_CTL (0x882dc4)
/* MAC timer, usec, for packet lifetime */
@@ -692,6 +701,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 +741,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;
@@ -943,8 +955,13 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
u64 *cookie);
+#if defined(CONFIG_WIL6210_DEBUGFS)
int wil6210_debugfs_init(struct wil6210_priv *wil);
void wil6210_debugfs_remove(struct wil6210_priv *wil);
+#else
+static inline int wil6210_debugfs_init(struct wil6210_priv *wil) { return 0; }
+static inline void wil6210_debugfs_remove(struct wil6210_priv *wil) {}
+#endif
int wil6210_sysfs_init(struct wil6210_priv *wil);
void wil6210_sysfs_remove(struct wil6210_priv *wil);
int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
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..5263ee717a4f 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -36,6 +36,11 @@
#define WMI_PROX_RANGE_NUM (3)
#define WMI_MAX_LOSS_DMG_BEACONS (20)
#define MAX_NUM_OF_SECTORS (128)
+#define WMI_SCHED_MAX_ALLOCS_PER_CMD (4)
+#define WMI_RF_DTYPE_LENGTH (3)
+#define WMI_RF_ETYPE_LENGTH (3)
+#define WMI_RF_RX2TX_LENGTH (3)
+#define WMI_RF_ETYPE_VAL_PER_RANGE (5)
/* Mailbox interface
* used for commands and events
@@ -52,14 +57,20 @@ enum wmi_mid {
* the host
*/
enum wmi_fw_capability {
- WMI_FW_CAPABILITY_FTM = 0,
- WMI_FW_CAPABILITY_PS_CONFIG = 1,
- WMI_FW_CAPABILITY_RF_SECTORS = 2,
- WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3,
- WMI_FW_CAPABILITY_DISABLE_AP_SME = 4,
- WMI_FW_CAPABILITY_WMI_ONLY = 5,
- WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7,
- WMI_FW_CAPABILITY_D3_SUSPEND = 8,
+ WMI_FW_CAPABILITY_FTM = 0,
+ WMI_FW_CAPABILITY_PS_CONFIG = 1,
+ WMI_FW_CAPABILITY_RF_SECTORS = 2,
+ WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3,
+ WMI_FW_CAPABILITY_DISABLE_AP_SME = 4,
+ WMI_FW_CAPABILITY_WMI_ONLY = 5,
+ WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7,
+ WMI_FW_CAPABILITY_D3_SUSPEND = 8,
+ WMI_FW_CAPABILITY_LONG_RANGE = 9,
+ WMI_FW_CAPABILITY_FIXED_SCHEDULING = 10,
+ WMI_FW_CAPABILITY_MULTI_DIRECTED_OMNIS = 11,
+ WMI_FW_CAPABILITY_RSSI_REPORTING = 12,
+ WMI_FW_CAPABILITY_SET_SILENT_RSSI_TABLE = 13,
+ WMI_FW_CAPABILITY_LO_POWER_CALIB_FROM_OTP = 14,
WMI_FW_CAPABILITY_MAX,
};
@@ -79,6 +90,7 @@ enum wmi_command_id {
WMI_START_SCAN_CMDID = 0x07,
WMI_SET_BSS_FILTER_CMDID = 0x09,
WMI_SET_PROBED_SSID_CMDID = 0x0A,
+ /* deprecated */
WMI_SET_LISTEN_INT_CMDID = 0x0B,
WMI_BCON_CTRL_CMDID = 0x0F,
WMI_ADD_CIPHER_KEY_CMDID = 0x16,
@@ -93,26 +105,28 @@ enum wmi_command_id {
WMI_ECHO_CMDID = 0x803,
WMI_DEEP_ECHO_CMDID = 0x804,
WMI_CONFIG_MAC_CMDID = 0x805,
+ /* deprecated */
WMI_CONFIG_PHY_DEBUG_CMDID = 0x806,
WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x808,
WMI_PHY_GET_STATISTICS_CMDID = 0x809,
+ /* deprecated */
WMI_FS_TUNE_CMDID = 0x80A,
+ /* deprecated */
WMI_CORR_MEASURE_CMDID = 0x80B,
WMI_READ_RSSI_CMDID = 0x80C,
WMI_TEMP_SENSE_CMDID = 0x80E,
WMI_DC_CALIB_CMDID = 0x80F,
+ /* deprecated */
WMI_SEND_TONE_CMDID = 0x810,
+ /* deprecated */
WMI_IQ_TX_CALIB_CMDID = 0x811,
+ /* deprecated */
WMI_IQ_RX_CALIB_CMDID = 0x812,
- WMI_SET_UCODE_IDLE_CMDID = 0x813,
WMI_SET_WORK_MODE_CMDID = 0x815,
WMI_LO_LEAKAGE_CALIB_CMDID = 0x816,
- WMI_MARLON_R_READ_CMDID = 0x818,
- WMI_MARLON_R_WRITE_CMDID = 0x819,
- WMI_MARLON_R_TXRX_SEL_CMDID = 0x81A,
- MAC_IO_STATIC_PARAMS_CMDID = 0x81B,
- MAC_IO_DYNAMIC_PARAMS_CMDID = 0x81C,
+ WMI_LO_POWER_CALIB_FROM_OTP_CMDID = 0x817,
WMI_SILENT_RSSI_CALIB_CMDID = 0x81D,
+ /* deprecated */
WMI_RF_RX_TEST_CMDID = 0x81E,
WMI_CFG_RX_CHAIN_CMDID = 0x820,
WMI_VRING_CFG_CMDID = 0x821,
@@ -126,11 +140,6 @@ enum wmi_command_id {
WMI_SET_PCP_CHANNEL_CMDID = 0x829,
WMI_GET_PCP_CHANNEL_CMDID = 0x82A,
WMI_SW_TX_REQ_CMDID = 0x82B,
- WMI_READ_MAC_RXQ_CMDID = 0x830,
- WMI_READ_MAC_TXQ_CMDID = 0x831,
- WMI_WRITE_MAC_RXQ_CMDID = 0x832,
- WMI_WRITE_MAC_TXQ_CMDID = 0x833,
- WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x834,
WMI_MLME_PUSH_CMDID = 0x835,
WMI_BEAMFORMING_MGMT_CMDID = 0x836,
WMI_BF_TXSS_MGMT_CMDID = 0x837,
@@ -144,9 +153,13 @@ enum wmi_command_id {
WMI_MAINTAIN_RESUME_CMDID = 0x851,
WMI_RS_MGMT_CMDID = 0x852,
WMI_RF_MGMT_CMDID = 0x853,
- WMI_OTP_READ_CMDID = 0x856,
- WMI_OTP_WRITE_CMDID = 0x857,
+ WMI_RF_XPM_READ_CMDID = 0x856,
+ WMI_RF_XPM_WRITE_CMDID = 0x857,
WMI_LED_CFG_CMDID = 0x858,
+ WMI_SET_CONNECT_SNR_THR_CMDID = 0x85B,
+ WMI_SET_ACTIVE_SILENT_RSSI_TABLE_CMDID = 0x85C,
+ WMI_RF_PWR_ON_DELAY_CMDID = 0x85D,
+ WMI_SET_HIGH_POWER_TABLE_PARAMS_CMDID = 0x85E,
/* Performance monitoring commands */
WMI_BF_CTRL_CMDID = 0x862,
WMI_NOTIFY_REQ_CMDID = 0x863,
@@ -154,7 +167,6 @@ enum wmi_command_id {
WMI_GET_RF_STATUS_CMDID = 0x866,
WMI_GET_BASEBAND_TYPE_CMDID = 0x867,
WMI_UNIT_TEST_CMDID = 0x900,
- WMI_HICCUP_CMDID = 0x901,
WMI_FLASH_READ_CMDID = 0x902,
WMI_FLASH_WRITE_CMDID = 0x903,
/* Power management */
@@ -174,16 +186,6 @@ enum wmi_command_id {
WMI_GET_PCP_FACTOR_CMDID = 0x91B,
/* Power Save Configuration Commands */
WMI_PS_DEV_PROFILE_CFG_CMDID = 0x91C,
- /* Not supported yet */
- WMI_PS_DEV_CFG_CMDID = 0x91D,
- /* Not supported yet */
- WMI_PS_DEV_CFG_READ_CMDID = 0x91E,
- /* Per MAC Power Save Configuration commands
- * Not supported yet
- */
- WMI_PS_MID_CFG_CMDID = 0x91F,
- /* Not supported yet */
- WMI_PS_MID_CFG_READ_CMDID = 0x920,
WMI_RS_CFG_CMDID = 0x921,
WMI_GET_DETAILED_RS_RES_CMDID = 0x922,
WMI_AOA_MEAS_CMDID = 0x923,
@@ -194,13 +196,16 @@ enum wmi_command_id {
WMI_DEL_STA_CMDID = 0x936,
WMI_SET_THERMAL_THROTTLING_CFG_CMDID = 0x940,
WMI_GET_THERMAL_THROTTLING_CFG_CMDID = 0x941,
+ /* Read Power Save profile type */
+ WMI_PS_DEV_PROFILE_CFG_READ_CMDID = 0x942,
WMI_TOF_SESSION_START_CMDID = 0x991,
WMI_TOF_GET_CAPABILITIES_CMDID = 0x992,
WMI_TOF_SET_LCR_CMDID = 0x993,
WMI_TOF_SET_LCI_CMDID = 0x994,
- WMI_TOF_CHANNEL_INFO_CMDID = 0x995,
+ WMI_TOF_CFG_RESPONDER_CMDID = 0x996,
WMI_TOF_SET_TX_RX_OFFSET_CMDID = 0x997,
WMI_TOF_GET_TX_RX_OFFSET_CMDID = 0x998,
+ WMI_TOF_CHANNEL_INFO_CMDID = 0x999,
WMI_GET_RF_SECTOR_PARAMS_CMDID = 0x9A0,
WMI_SET_RF_SECTOR_PARAMS_CMDID = 0x9A1,
WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID = 0x9A2,
@@ -209,12 +214,20 @@ enum wmi_command_id {
WMI_PRIO_TX_SECTORS_ORDER_CMDID = 0x9A5,
WMI_PRIO_TX_SECTORS_NUMBER_CMDID = 0x9A6,
WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_CMDID = 0x9A7,
+ WMI_SCHEDULING_SCHEME_CMDID = 0xA01,
+ WMI_FIXED_SCHEDULING_CONFIG_CMDID = 0xA02,
+ WMI_ENABLE_FIXED_SCHEDULING_CMDID = 0xA03,
+ WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_CMDID = 0xA04,
+ WMI_SET_LONG_RANGE_CONFIG_CMDID = 0xA05,
WMI_SET_MAC_ADDRESS_CMDID = 0xF003,
WMI_ABORT_SCAN_CMDID = 0xF007,
WMI_SET_PROMISCUOUS_MODE_CMDID = 0xF041,
+ /* deprecated */
WMI_GET_PMK_CMDID = 0xF048,
WMI_SET_PASSPHRASE_CMDID = 0xF049,
+ /* deprecated */
WMI_SEND_ASSOC_RES_CMDID = 0xF04A,
+ /* deprecated */
WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xF04B,
WMI_MAC_ADDR_REQ_CMDID = 0xF04D,
WMI_FW_VER_CMDID = 0xF04E,
@@ -440,11 +453,6 @@ struct wmi_rf_mgmt_cmd {
__le32 rf_mgmt_type;
} __packed;
-/* WMI_RF_RX_TEST_CMDID */
-struct wmi_rf_rx_test_cmd {
- __le32 sector;
-} __packed;
-
/* WMI_CORR_MEASURE_CMDID */
struct wmi_corr_measure_cmd {
__le32 freq_mhz;
@@ -657,6 +665,20 @@ struct wmi_bcast_vring_cfg_cmd {
struct wmi_bcast_vring_cfg vring_cfg;
} __packed;
+/* WMI_LO_POWER_CALIB_FROM_OTP_CMDID */
+struct wmi_lo_power_calib_from_otp_cmd {
+ /* index to read from OTP. zero based */
+ u8 index;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_LO_POWER_CALIB_FROM_OTP_EVENTID */
+struct wmi_lo_power_calib_from_otp_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
/* WMI_VRING_BA_EN_CMDID */
struct wmi_vring_ba_en_cmd {
u8 ringid;
@@ -692,6 +714,24 @@ enum wmi_sniffer_cfg_mode {
WMI_SNIFFER_ON = 0x01,
};
+/* WMI_SILENT_RSSI_TABLE */
+enum wmi_silent_rssi_table {
+ RF_TEMPERATURE_CALIB_DEFAULT_DB = 0x00,
+ RF_TEMPERATURE_CALIB_HIGH_POWER_DB = 0x01,
+};
+
+/* WMI_SILENT_RSSI_STATUS */
+enum wmi_silent_rssi_status {
+ SILENT_RSSI_SUCCESS = 0x00,
+ SILENT_RSSI_FAILURE = 0x01,
+};
+
+/* WMI_SET_ACTIVE_SILENT_RSSI_TABLE_CMDID */
+struct wmi_set_active_silent_rssi_table_cmd {
+ /* enum wmi_silent_rssi_table */
+ __le32 table;
+} __packed;
+
enum wmi_sniffer_cfg_phy_info_mode {
WMI_SNIFFER_PHY_INFO_DISABLED = 0x00,
WMI_SNIFFER_PHY_INFO_ENABLED = 0x01,
@@ -835,18 +875,85 @@ struct wmi_echo_cmd {
__le32 value;
} __packed;
-/* WMI_OTP_READ_CMDID */
-struct wmi_otp_read_cmd {
- __le32 addr;
- __le32 size;
- __le32 values;
+/* WMI_RF_PWR_ON_DELAY_CMDID
+ * set FW time parameters used through RF resetting
+ * RF reset consists of bringing its power down for a period of time, then
+ * bringing the power up
+ * Returned event: WMI_RF_PWR_ON_DELAY_RSP_EVENTID
+ */
+struct wmi_rf_pwr_on_delay_cmd {
+ /* time in usec the FW waits after bringing the RF PWR down,
+ * set 0 for default
+ */
+ __le16 down_delay_usec;
+ /* time in usec the FW waits after bringing the RF PWR up,
+ * set 0 for default
+ */
+ __le16 up_delay_usec;
+} __packed;
+
+/* \WMI_SET_HIGH_POWER_TABLE_PARAMS_CMDID
+ * This API controls the Tx and Rx gain over temperature.
+ * It controls the Tx D-type, Rx D-type and Rx E-type amplifiers.
+ * It also controls the Tx gain index, by controlling the Rx to Tx gain index
+ * offset.
+ * The control is divided by 3 temperature values to 4 temperature ranges.
+ * Each parameter uses its own temperature values.
+ * Returned event: WMI_SET_HIGH_POWER_TABLE_PARAMS_EVENTID
+ */
+struct wmi_set_high_power_table_params_cmd {
+ /* Temperature range for Tx D-type parameters */
+ u8 tx_dtype_temp[WMI_RF_DTYPE_LENGTH];
+ u8 reserved0;
+ /* Tx D-type values to be used for each temperature range */
+ __le32 tx_dtype_conf[WMI_RF_DTYPE_LENGTH + 1];
+ /* Temperature range for Rx D-type parameters */
+ u8 rx_dtype_temp[WMI_RF_DTYPE_LENGTH];
+ u8 reserved1;
+ /* Rx D-type values to be used for each temperature range */
+ __le32 rx_dtype_conf[WMI_RF_DTYPE_LENGTH + 1];
+ /* Temperature range for Rx E-type parameters */
+ u8 rx_etype_temp[WMI_RF_ETYPE_LENGTH];
+ u8 reserved2;
+ /* Rx E-type values to be used for each temperature range.
+ * The last 4 values of any range are the first 4 values of the next
+ * range and so on
+ */
+ __le32 rx_etype_conf[WMI_RF_ETYPE_VAL_PER_RANGE + WMI_RF_ETYPE_LENGTH];
+ /* Temperature range for rx_2_tx_offs parameters */
+ u8 rx_2_tx_temp[WMI_RF_RX2TX_LENGTH];
+ u8 reserved3;
+ /* Rx to Tx gain index offset */
+ s8 rx_2_tx_offs[WMI_RF_RX2TX_LENGTH + 1];
+} __packed;
+
+/* CMD: WMI_RF_XPM_READ_CMDID */
+struct wmi_rf_xpm_read_cmd {
+ u8 rf_id;
+ u8 reserved[3];
+ /* XPM bit start address in range [0,8191]bits - rounded by FW to
+ * multiple of 8bits
+ */
+ __le32 xpm_bit_address;
+ __le32 num_bytes;
} __packed;
-/* WMI_OTP_WRITE_CMDID */
-struct wmi_otp_write_cmd {
- __le32 addr;
- __le32 size;
- __le32 values;
+/* CMD: WMI_RF_XPM_WRITE_CMDID */
+struct wmi_rf_xpm_write_cmd {
+ u8 rf_id;
+ u8 reserved0[3];
+ /* XPM bit start address in range [0,8191]bits - rounded by FW to
+ * multiple of 8bits
+ */
+ __le32 xpm_bit_address;
+ __le32 num_bytes;
+ /* boolean flag indicating whether FW should verify the write
+ * operation
+ */
+ u8 verify;
+ u8 reserved1[3];
+ /* actual size=num_bytes */
+ u8 data_bytes[0];
} __packed;
/* WMI_TEMP_SENSE_CMDID
@@ -989,19 +1096,26 @@ struct wmi_ftm_dest_info {
*/
__le16 burst_period;
u8 dst_mac[WMI_MAC_LEN];
- __le16 reserved;
+ u8 reserved;
+ u8 num_burst_per_aoa_meas;
} __packed;
/* WMI_TOF_SESSION_START_CMDID */
struct wmi_tof_session_start_cmd {
__le32 session_id;
- u8 num_of_aoa_measures;
+ u8 reserved1;
u8 aoa_type;
__le16 num_of_dest;
u8 reserved[4];
struct wmi_ftm_dest_info ftm_dest_info[0];
} __packed;
+/* WMI_TOF_CFG_RESPONDER_CMDID */
+struct wmi_tof_cfg_responder_cmd {
+ u8 enable;
+ u8 reserved[3];
+} __packed;
+
enum wmi_tof_channel_info_report_type {
WMI_TOF_CHANNEL_INFO_TYPE_CIR = 0x1,
WMI_TOF_CHANNEL_INFO_TYPE_RSSI = 0x2,
@@ -1022,7 +1136,99 @@ struct wmi_tof_set_tx_rx_offset_cmd {
__le32 tx_offset;
/* RX delay offset */
__le32 rx_offset;
- __le32 reserved[2];
+ /* Mask to define which RFs to configure. 0 means all RFs */
+ __le32 rf_mask;
+ /* Offset to strongest tap of CIR */
+ __le32 precursor;
+} __packed;
+
+/* WMI_TOF_GET_TX_RX_OFFSET_CMDID */
+struct wmi_tof_get_tx_rx_offset_cmd {
+ /* rf index to read offsets from */
+ u8 rf_index;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_FIXED_SCHEDULING_CONFIG_CMDID */
+struct wmi_map_mcs_to_schd_params {
+ u8 mcs;
+ /* time in usec from start slot to start tx flow - default 15 */
+ u8 time_in_usec_before_initiate_tx;
+ /* RD enable - if yes consider RD according to STA mcs */
+ u8 rd_enabled;
+ u8 reserved;
+ /* time in usec from start slot to stop vring */
+ __le16 time_in_usec_to_stop_vring;
+ /* timeout to force flush from start of slot */
+ __le16 flush_to_in_usec;
+ /* per mcs the mac buffer limit size in bytes */
+ __le32 mac_buff_size_in_bytes;
+} __packed;
+
+/* WMI_FIXED_SCHEDULING_CONFIG_COMPLETE_EVENTID */
+struct wmi_fixed_scheduling_config_complete_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
+#define WMI_NUM_MCS (13)
+
+/* WMI_FIXED_SCHEDULING_CONFIG_CMDID */
+struct wmi_fixed_scheduling_config_cmd {
+ /* defaults in the SAS table */
+ struct wmi_map_mcs_to_schd_params mcs_to_schd_params_map[WMI_NUM_MCS];
+ /* default 150 uSec */
+ __le16 max_sta_rd_ppdu_duration_in_usec;
+ /* default 300 uSec */
+ __le16 max_sta_grant_ppdu_duration_in_usec;
+ /* default 1000 uSec */
+ __le16 assoc_slot_duration_in_usec;
+ /* default 360 uSec */
+ __le16 virtual_slot_duration_in_usec;
+ /* each this field value slots start with grant frame to the station
+ * - default 2
+ */
+ u8 number_of_ap_slots_for_initiate_grant;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_ENABLE_FIXED_SCHEDULING_CMDID */
+struct wmi_enable_fixed_scheduling_cmd {
+ __le32 reserved;
+} __packed;
+
+/* WMI_ENABLE_FIXED_SCHEDULING_COMPLETE_EVENTID */
+struct wmi_enable_fixed_scheduling_complete_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_CMDID */
+struct wmi_set_multi_directed_omnis_config_cmd {
+ /* number of directed omnis at destination AP */
+ u8 dest_ap_num_directed_omnis;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_EVENTID */
+struct wmi_set_multi_directed_omnis_config_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_SET_LONG_RANGE_CONFIG_CMDID */
+struct wmi_set_long_range_config_cmd {
+ __le32 reserved;
+} __packed;
+
+/* WMI_SET_LONG_RANGE_CONFIG_COMPLETE_EVENTID */
+struct wmi_set_long_range_config_complete_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
} __packed;
/* WMI Events
@@ -1038,19 +1244,22 @@ enum wmi_event_id {
WMI_FW_READY_EVENTID = 0x1801,
WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x200,
WMI_ECHO_RSP_EVENTID = 0x1803,
+ /* deprecated */
WMI_FS_TUNE_DONE_EVENTID = 0x180A,
+ /* deprecated */
WMI_CORR_MEASURE_EVENTID = 0x180B,
WMI_READ_RSSI_EVENTID = 0x180C,
WMI_TEMP_SENSE_DONE_EVENTID = 0x180E,
WMI_DC_CALIB_DONE_EVENTID = 0x180F,
+ /* deprecated */
WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811,
+ /* deprecated */
WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812,
WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815,
WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816,
- WMI_MARLON_R_READ_DONE_EVENTID = 0x1818,
- WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819,
- WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181A,
+ WMI_LO_POWER_CALIB_FROM_OTP_EVENTID = 0x1817,
WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181D,
+ /* deprecated */
WMI_RF_RX_TEST_DONE_EVENTID = 0x181E,
WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820,
WMI_VRING_CFG_DONE_EVENTID = 0x1821,
@@ -1061,11 +1270,6 @@ enum wmi_event_id {
WMI_GET_SSID_EVENTID = 0x1828,
WMI_GET_PCP_CHANNEL_EVENTID = 0x182A,
WMI_SW_TX_COMPLETE_EVENTID = 0x182B,
- WMI_READ_MAC_RXQ_EVENTID = 0x1830,
- WMI_READ_MAC_TXQ_EVENTID = 0x1831,
- WMI_WRITE_MAC_RXQ_EVENTID = 0x1832,
- WMI_WRITE_MAC_TXQ_EVENTID = 0x1833,
- WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834,
WMI_BEAMFORMING_MGMT_DONE_EVENTID = 0x1836,
WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837,
WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839,
@@ -1076,8 +1280,12 @@ enum wmi_event_id {
WMI_TX_MGMT_PACKET_EVENTID = 0x1841,
WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID = 0x1842,
WMI_LINK_MAINTAIN_CFG_READ_DONE_EVENTID = 0x1843,
- WMI_OTP_READ_RESULT_EVENTID = 0x1856,
+ WMI_RF_XPM_READ_RESULT_EVENTID = 0x1856,
+ WMI_RF_XPM_WRITE_RESULT_EVENTID = 0x1857,
WMI_LED_CFG_DONE_EVENTID = 0x1858,
+ WMI_SET_SILENT_RSSI_TABLE_DONE_EVENTID = 0x185C,
+ WMI_RF_PWR_ON_DELAY_RSP_EVENTID = 0x185D,
+ WMI_SET_HIGH_POWER_TABLE_PARAMS_EVENTID = 0x185E,
/* Performance monitoring events */
WMI_DATA_PORT_OPEN_EVENTID = 0x1860,
WMI_WBE_LINK_DOWN_EVENTID = 0x1861,
@@ -1106,14 +1314,6 @@ enum wmi_event_id {
WMI_PCP_FACTOR_EVENTID = 0x191A,
/* Power Save Configuration Events */
WMI_PS_DEV_PROFILE_CFG_EVENTID = 0x191C,
- /* Not supported yet */
- WMI_PS_DEV_CFG_EVENTID = 0x191D,
- /* Not supported yet */
- WMI_PS_DEV_CFG_READ_EVENTID = 0x191E,
- /* Not supported yet */
- WMI_PS_MID_CFG_EVENTID = 0x191F,
- /* Not supported yet */
- WMI_PS_MID_CFG_READ_EVENTID = 0x1920,
WMI_RS_CFG_DONE_EVENTID = 0x1921,
WMI_GET_DETAILED_RS_RES_EVENTID = 0x1922,
WMI_AOA_MEAS_EVENTID = 0x1923,
@@ -1122,14 +1322,17 @@ enum wmi_event_id {
WMI_GET_MGMT_RETRY_LIMIT_EVENTID = 0x1931,
WMI_SET_THERMAL_THROTTLING_CFG_EVENTID = 0x1940,
WMI_GET_THERMAL_THROTTLING_CFG_EVENTID = 0x1941,
+ /* return the Power Save profile */
+ WMI_PS_DEV_PROFILE_CFG_READ_EVENTID = 0x1942,
WMI_TOF_SESSION_END_EVENTID = 0x1991,
WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992,
WMI_TOF_SET_LCR_EVENTID = 0x1993,
WMI_TOF_SET_LCI_EVENTID = 0x1994,
WMI_TOF_FTM_PER_DEST_RES_EVENTID = 0x1995,
- WMI_TOF_CHANNEL_INFO_EVENTID = 0x1996,
+ WMI_TOF_CFG_RESPONDER_EVENTID = 0x1996,
WMI_TOF_SET_TX_RX_OFFSET_EVENTID = 0x1997,
WMI_TOF_GET_TX_RX_OFFSET_EVENTID = 0x1998,
+ WMI_TOF_CHANNEL_INFO_EVENTID = 0x1999,
WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID = 0x19A0,
WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID = 0x19A1,
WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID = 0x19A2,
@@ -1138,12 +1341,18 @@ enum wmi_event_id {
WMI_PRIO_TX_SECTORS_ORDER_EVENTID = 0x19A5,
WMI_PRIO_TX_SECTORS_NUMBER_EVENTID = 0x19A6,
WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_EVENTID = 0x19A7,
+ WMI_SCHEDULING_SCHEME_EVENTID = 0x1A01,
+ WMI_FIXED_SCHEDULING_CONFIG_COMPLETE_EVENTID = 0x1A02,
+ WMI_ENABLE_FIXED_SCHEDULING_COMPLETE_EVENTID = 0x1A03,
+ WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_EVENTID = 0x1A04,
+ WMI_SET_LONG_RANGE_CONFIG_COMPLETE_EVENTID = 0x1A05,
WMI_SET_CHANNEL_EVENTID = 0x9000,
WMI_ASSOC_REQ_EVENTID = 0x9001,
WMI_EAPOL_RX_EVENTID = 0x9002,
WMI_MAC_ADDR_RESP_EVENTID = 0x9003,
WMI_FW_VER_EVENTID = 0x9004,
WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENTID = 0x9005,
+ WMI_COMMAND_NOT_SUPPORTED_EVENTID = 0xFFFF,
};
/* Events data structures */
@@ -1200,7 +1409,7 @@ struct wmi_fw_ver_event {
__le32 bl_minor;
__le32 bl_subminor;
__le32 bl_build;
- /* The number of entries in the FW capabilies array */
+ /* The number of entries in the FW capabilities array */
u8 fw_capabilities_len;
u8 reserved[3];
/* FW capabilities info
@@ -1245,7 +1454,9 @@ struct wmi_get_rf_status_event {
__le32 board_file_platform_type;
/* board file version */
__le32 board_file_version;
- __le32 reserved[2];
+ /* enabled XIFs bit vector */
+ __le32 enabled_xif_vector;
+ __le32 reserved;
} __packed;
/* WMI_GET_BASEBAND_TYPE_EVENTID */
@@ -1299,6 +1510,9 @@ struct wmi_ready_event {
/* enum wmi_phy_capability */
u8 phy_capability;
u8 numof_additional_mids;
+ /* rfc read calibration result. 5..15 */
+ u8 rfc_read_calib_result;
+ u8 reserved[3];
} __packed;
/* WMI_NOTIFY_REQ_DONE_EVENTID */
@@ -1306,7 +1520,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;
@@ -1576,7 +1791,7 @@ struct wmi_sw_tx_complete_event {
u8 reserved[3];
} __packed;
-/* WMI_CORR_MEASURE_EVENTID */
+/* WMI_CORR_MEASURE_EVENTID - deprecated */
struct wmi_corr_measure_event {
/* signed */
__le32 i;
@@ -1602,31 +1817,35 @@ 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;
__le16 status;
__le32 len;
- /* Not resolved when == 0xFFFFFFFF ==> Broadcast to all MIDS */
+ /* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
u8 qid;
- /* Not resolved when == 0xFFFFFFFF ==> Broadcast to all MIDS */
+ /* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
u8 mid;
u8 cid;
/* From Radio MNGR */
u8 channel;
} __packed;
-/* wmi_otp_read_write_cmd */
-struct wmi_otp_read_write_cmd {
- __le32 addr;
- __le32 size;
- u8 values[0];
+/* EVENT: WMI_RF_XPM_READ_RESULT_EVENTID */
+struct wmi_rf_xpm_read_result_event {
+ /* enum wmi_fw_status_e - success=0 or fail=1 */
+ u8 status;
+ u8 reserved[3];
+ /* requested num_bytes of data */
+ u8 data_bytes[0];
} __packed;
-/* WMI_OTP_READ_RESULT_EVENTID */
-struct wmi_otp_read_result_event {
- u8 payload[0];
+/* EVENT: WMI_RF_XPM_WRITE_RESULT_EVENTID */
+struct wmi_rf_xpm_write_result_event {
+ /* enum wmi_fw_status_e - success=0 or fail=1 */
+ u8 status;
+ u8 reserved[3];
} __packed;
/* WMI_TX_MGMT_PACKET_EVENTID */
@@ -1645,6 +1864,20 @@ struct wmi_echo_rsp_event {
__le32 echoed_value;
} __packed;
+/* WMI_RF_PWR_ON_DELAY_RSP_EVENTID */
+struct wmi_rf_pwr_on_delay_rsp_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_SET_HIGH_POWER_TABLE_PARAMS_EVENTID */
+struct wmi_set_high_power_table_params_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
/* WMI_TEMP_SENSE_DONE_EVENTID
*
* Measure MAC and radio temperatures
@@ -1722,14 +1955,22 @@ struct wmi_led_cfg_cmd {
u8 reserved;
} __packed;
+/* \WMI_SET_CONNECT_SNR_THR_CMDID */
+struct wmi_set_connect_snr_thr_cmd {
+ u8 enable;
+ u8 reserved;
+ /* 1/4 Db units */
+ __le16 omni_snr_thr;
+ /* 1/4 Db units */
+ __le16 direct_snr_thr;
+} __packed;
+
/* WMI_LED_CFG_DONE_EVENTID */
struct wmi_led_cfg_done_event {
/* led config status */
__le32 status;
} __packed;
-#define WMI_NUM_MCS (13)
-
/* Rate search parameters configuration per connection */
struct wmi_rs_cfg {
/* The maximal allowed PER for each MCS
@@ -1754,6 +1995,98 @@ struct wmi_rs_cfg {
__le32 mcs_en_vec;
} __packed;
+/* Slot types */
+enum wmi_sched_scheme_slot_type {
+ WMI_SCHED_SLOT_SP = 0x0,
+ WMI_SCHED_SLOT_CBAP = 0x1,
+ WMI_SCHED_SLOT_IDLE = 0x2,
+ WMI_SCHED_SLOT_ANNOUNCE_NO_ACK = 0x3,
+ WMI_SCHED_SLOT_DISCOVERY = 0x4,
+};
+
+enum wmi_sched_scheme_slot_flags {
+ WMI_SCHED_SCHEME_SLOT_PERIODIC = 0x1,
+};
+
+struct wmi_sched_scheme_slot {
+ /* in microsecond */
+ __le32 tbtt_offset;
+ /* wmi_sched_scheme_slot_flags */
+ u8 flags;
+ /* wmi_sched_scheme_slot_type */
+ u8 type;
+ /* in microsecond */
+ __le16 duration;
+ /* frame_exchange_sequence_duration */
+ __le16 tx_op;
+ /* time in microseconds between two consecutive slots
+ * relevant only if flag WMI_SCHED_SCHEME_SLOT_PERIODIC set
+ */
+ __le16 period;
+ /* relevant only if flag WMI_SCHED_SCHEME_SLOT_PERIODIC set
+ * number of times to repeat allocation
+ */
+ u8 num_of_blocks;
+ /* relevant only if flag WMI_SCHED_SCHEME_SLOT_PERIODIC set
+ * every idle_period allocation will be idle
+ */
+ u8 idle_period;
+ u8 src_aid;
+ u8 dest_aid;
+ __le32 reserved;
+} __packed;
+
+enum wmi_sched_scheme_flags {
+ /* should not be set when clearing scheduling scheme */
+ WMI_SCHED_SCHEME_ENABLE = 0x01,
+ WMI_SCHED_PROTECTED_SP = 0x02,
+ /* should be set only on first WMI fragment of scheme */
+ WMI_SCHED_FIRST = 0x04,
+ /* should be set only on last WMI fragment of scheme */
+ WMI_SCHED_LAST = 0x08,
+ WMI_SCHED_IMMEDIATE_START = 0x10,
+};
+
+enum wmi_sched_scheme_advertisment {
+ /* ESE is not advertised at all, STA has to be configured with WMI
+ * also
+ */
+ WMI_ADVERTISE_ESE_DISABLED = 0x0,
+ WMI_ADVERTISE_ESE_IN_BEACON = 0x1,
+ WMI_ADVERTISE_ESE_IN_ANNOUNCE_FRAME = 0x2,
+};
+
+/* WMI_SCHEDULING_SCHEME_CMD */
+struct wmi_scheduling_scheme_cmd {
+ u8 serial_num;
+ /* wmi_sched_scheme_advertisment */
+ u8 ese_advertisment;
+ /* wmi_sched_scheme_flags */
+ __le16 flags;
+ u8 num_allocs;
+ u8 reserved[3];
+ __le64 start_tbtt;
+ /* allocations list */
+ struct wmi_sched_scheme_slot allocs[WMI_SCHED_MAX_ALLOCS_PER_CMD];
+} __packed;
+
+enum wmi_sched_scheme_failure_type {
+ WMI_SCHED_SCHEME_FAILURE_NO_ERROR = 0x00,
+ WMI_SCHED_SCHEME_FAILURE_OLD_START_TSF_ERR = 0x01,
+};
+
+/* WMI_SCHEDULING_SCHEME_EVENTID */
+struct wmi_scheduling_scheme_event {
+ /* wmi_fw_status_e */
+ u8 status;
+ /* serial number given in command */
+ u8 serial_num;
+ /* wmi_sched_scheme_failure_type */
+ u8 failure_type;
+ /* alignment to 32b */
+ u8 reserved[1];
+} __packed;
+
/* WMI_RS_CFG_CMDID */
struct wmi_rs_cfg_cmd {
/* connection id */
@@ -1971,6 +2304,19 @@ enum wmi_ps_profile_type {
WMI_PS_PROFILE_TYPE_LOW_LATENCY_PS = 0x03,
};
+/* WMI_PS_DEV_PROFILE_CFG_READ_CMDID */
+struct wmi_ps_dev_profile_cfg_read_cmd {
+ /* reserved */
+ __le32 reserved;
+} __packed;
+
+/* WMI_PS_DEV_PROFILE_CFG_READ_EVENTID */
+struct wmi_ps_dev_profile_cfg_read_event {
+ /* wmi_ps_profile_type_e */
+ u8 ps_profile;
+ u8 reserved[3];
+} __packed;
+
/* WMI_PS_DEV_PROFILE_CFG_CMDID
*
* Power save profile to be used by the device
@@ -2019,157 +2365,6 @@ enum wmi_ps_d3_resp_policy {
WMI_PS_D3_RESP_POLICY_APPROVED = 0x02,
};
-/* Device common power save configurations */
-struct wmi_ps_dev_cfg {
- /* lowest level of PS allowed while unassociated, enum wmi_ps_level_e
- */
- u8 ps_unassoc_min_level;
- /* lowest deep sleep clock level while nonassoc, enum
- * wmi_ps_deep_sleep_clk_level_e
- */
- u8 ps_unassoc_deep_sleep_min_level;
- /* lowest level of PS allowed while associated, enum wmi_ps_level_e */
- u8 ps_assoc_min_level;
- /* lowest deep sleep clock level while assoc, enum
- * wmi_ps_deep_sleep_clk_level_e
- */
- u8 ps_assoc_deep_sleep_min_level;
- /* enum wmi_ps_deep_sleep_clk_level_e */
- u8 ps_assoc_low_latency_ds_min_level;
- /* enum wmi_ps_d3_resp_policy_e */
- u8 ps_D3_response_policy;
- /* BOOL */
- u8 ps_D3_pm_pme_enabled;
- /* BOOL */
- u8 ps_halp_enable;
- u8 ps_deep_sleep_enter_thresh_msec;
- /* BOOL */
- u8 ps_voltage_scaling_en;
-} __packed;
-
-/* WMI_PS_DEV_CFG_CMDID
- *
- * Configure common Power Save parameters of the device and all MIDs.
- *
- * Returned event:
- * - WMI_PS_DEV_CFG_EVENTID
- */
-struct wmi_ps_dev_cfg_cmd {
- /* Device Power Save configuration to be applied */
- struct wmi_ps_dev_cfg ps_dev_cfg;
- /* alignment to 32b */
- u8 reserved[2];
-} __packed;
-
-/* WMI_PS_DEV_CFG_EVENTID */
-struct wmi_ps_dev_cfg_event {
- /* wmi_ps_cfg_cmd_status_e */
- __le32 status;
-} __packed;
-
-/* WMI_PS_DEV_CFG_READ_CMDID
- *
- * request to retrieve device Power Save configuration
- * (WMI_PS_DEV_CFG_CMD params)
- *
- * Returned event:
- * - WMI_PS_DEV_CFG_READ_EVENTID
- */
-struct wmi_ps_dev_cfg_read_cmd {
- __le32 reserved;
-} __packed;
-
-/* WMI_PS_DEV_CFG_READ_EVENTID */
-struct wmi_ps_dev_cfg_read_event {
- /* wmi_ps_cfg_cmd_status_e */
- __le32 status;
- /* Retrieved device Power Save configuration (WMI_PS_DEV_CFG_CMD
- * params)
- */
- struct wmi_ps_dev_cfg dev_ps_cfg;
- /* alignment to 32b */
- u8 reserved[2];
-} __packed;
-
-/* Per Mac Power Save configurations */
-struct wmi_ps_mid_cfg {
- /* Low power RX in BTI is enabled, BOOL */
- u8 beacon_lprx_enable;
- /* Sync to sector ID enabled, BOOL */
- u8 beacon_sync_to_sectorId_enable;
- /* Low power RX in DTI is enabled, BOOL */
- u8 frame_exchange_lprx_enable;
- /* Sleep Cycle while in scheduled PS, 1-31 */
- u8 scheduled_sleep_cycle_pow2;
- /* Stay Awake for k BIs every (sleep_cycle - k) BIs, 1-31 */
- u8 scheduled_num_of_awake_bis;
- u8 am_to_traffic_load_thresh_mbp;
- u8 traffic_to_am_load_thresh_mbps;
- u8 traffic_to_am_num_of_no_traffic_bis;
- /* BOOL */
- u8 continuous_traffic_psm;
- __le16 no_traffic_to_min_usec;
- __le16 no_traffic_to_max_usec;
- __le16 snoozing_sleep_interval_milisec;
- u8 max_no_data_awake_events;
- /* Trigger WEB after k failed beacons */
- u8 num_of_failed_beacons_rx_to_trigger_web;
- /* Trigger BF after k failed beacons */
- u8 num_of_failed_beacons_rx_to_trigger_bf;
- /* Trigger SOB after k successful beacons */
- u8 num_of_successful_beacons_rx_to_trigger_sob;
-} __packed;
-
-/* WMI_PS_MID_CFG_CMDID
- *
- * Configure Power Save parameters of a specific MID.
- * These parameters are relevant for the specific BSS this MID belongs to.
- *
- * Returned event:
- * - WMI_PS_MID_CFG_EVENTID
- */
-struct wmi_ps_mid_cfg_cmd {
- /* MAC ID */
- u8 mid;
- /* mid PS configuration to be applied */
- struct wmi_ps_mid_cfg ps_mid_cfg;
-} __packed;
-
-/* WMI_PS_MID_CFG_EVENTID */
-struct wmi_ps_mid_cfg_event {
- /* MAC ID */
- u8 mid;
- /* alignment to 32b */
- u8 reserved[3];
- /* wmi_ps_cfg_cmd_status_e */
- __le32 status;
-} __packed;
-
-/* WMI_PS_MID_CFG_READ_CMDID
- *
- * request to retrieve Power Save configuration of mid
- * (WMI_PS_MID_CFG_CMD params)
- *
- * Returned event:
- * - WMI_PS_MID_CFG_READ_EVENTID
- */
-struct wmi_ps_mid_cfg_read_cmd {
- /* MAC ID */
- u8 mid;
- /* alignment to 32b */
- u8 reserved[3];
-} __packed;
-
-/* WMI_PS_MID_CFG_READ_EVENTID */
-struct wmi_ps_mid_cfg_read_event {
- /* MAC ID */
- u8 mid;
- /* Retrieved MID Power Save configuration(WMI_PS_MID_CFG_CMD params) */
- struct wmi_ps_mid_cfg mid_ps_cfg;
- /* wmi_ps_cfg_cmd_status_e */
- __le32 status;
-} __packed;
-
#define WMI_AOA_MAX_DATA_SIZE (128)
enum wmi_aoa_meas_status {
@@ -2260,6 +2455,20 @@ struct wmi_tof_session_end_event {
u8 reserved[3];
} __packed;
+/* WMI_TOF_SET_LCI_EVENTID */
+struct wmi_tof_set_lci_event {
+ /* enum wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_TOF_SET_LCR_EVENTID */
+struct wmi_tof_set_lcr_event {
+ /* enum wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
/* Responder FTM Results */
struct wmi_responder_ftm_res {
u8 t1[6];
@@ -2313,10 +2522,19 @@ struct wmi_tof_ftm_per_dest_res_event {
__le32 tsf_sync;
/* actual received ftm per burst */
u8 actual_ftm_per_burst;
- u8 reserved0[7];
+ /* Measurments are from RFs, defined by the mask */
+ __le32 meas_rf_mask;
+ u8 reserved0[3];
struct wmi_responder_ftm_res responder_ftm_res[0];
} __packed;
+/* WMI_TOF_CFG_RESPONDER_EVENTID */
+struct wmi_tof_cfg_responder_event {
+ /* enum wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
enum wmi_tof_channel_info_type {
WMI_TOF_CHANNEL_INFO_AOA = 0x00,
WMI_TOF_CHANNEL_INFO_LCI = 0x01,
@@ -2353,12 +2571,15 @@ struct wmi_tof_set_tx_rx_offset_event {
struct wmi_tof_get_tx_rx_offset_event {
/* enum wmi_fw_status */
u8 status;
- u8 reserved1[3];
+ /* RF index used to read the offsets */
+ u8 rf_index;
+ u8 reserved1[2];
/* TX delay offset */
__le32 tx_offset;
/* RX delay offset */
__le32 rx_offset;
- __le32 reserved2[2];
+ /* Offset to strongest tap of CIR */
+ __le32 precursor;
} __packed;
/* Result status codes for WMI commands */
@@ -2621,4 +2842,23 @@ struct wmi_prio_tx_sectors_set_default_cfg_event {
u8 reserved[3];
} __packed;
+/* WMI_SET_SILENT_RSSI_TABLE_DONE_EVENTID */
+struct wmi_set_silent_rssi_table_done_event {
+ /* enum wmi_silent_rssi_status */
+ __le32 status;
+ /* enum wmi_silent_rssi_table */
+ __le32 table;
+} __packed;
+
+/* \WMI_COMMAND_NOT_SUPPORTED_EVENTID */
+struct wmi_command_not_supported_event {
+ /* device id */
+ u8 mid;
+ u8 reserved0;
+ __le16 command_id;
+ /* for UT command only, otherwise reserved */
+ __le16 command_subtype;
+ __le16 reserved1;
+} __packed;
+
#endif /* __WILOCITY_WMI_H__ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
index 70a6985334d5..da5826d788d6 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
@@ -4472,6 +4472,11 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
GFP_KERNEL);
} else if (ieee80211_is_action(mgmt->frame_control)) {
+ if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) {
+ brcmf_err("invalid action frame length\n");
+ err = -EINVAL;
+ goto exit;
+ }
af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
if (af_params == NULL) {
brcmf_err("unable to allocate frame\n");
diff --git a/drivers/net/wireless/cnss/Kconfig b/drivers/net/wireless/cnss/Kconfig
index 6faf9f1ef5d0..863f766bccdb 100644
--- a/drivers/net/wireless/cnss/Kconfig
+++ b/drivers/net/wireless/cnss/Kconfig
@@ -46,7 +46,6 @@ config CLD_HL_SDIO_CORE
select WEXT_CORE
select WEXT_SPY
select NL80211_TESTMODE
- depends on ARCH_MSM
depends on MMC
config CLD_LL_CORE
diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c
index 22c59d8c3c45..af92f00ca56e 100644
--- a/drivers/net/wireless/cnss/cnss_pci.c
+++ b/drivers/net/wireless/cnss/cnss_pci.c
@@ -83,6 +83,7 @@
#define QCA6180_DEVICE_ID (0x0041)
#define QCA6180_REV_ID_OFFSET (0x08)
+#define WLAN_EN_VREG_NAME "vdd-wlan-en"
#define WLAN_VREG_NAME "vdd-wlan"
#define WLAN_VREG_IO_NAME "vdd-wlan-io"
#define WLAN_VREG_XTAL_NAME "vdd-wlan-xtal"
@@ -114,6 +115,8 @@
#define WLAN_VREG_SP2T_MIN 2700000
#define POWER_ON_DELAY 2
+#define WLAN_VREG_IO_DELAY_MIN 100
+#define WLAN_VREG_IO_DELAY_MAX 1000
#define WLAN_ENABLE_DELAY 10
#define WLAN_RECOVERY_DELAY 1
#define PCIE_ENABLE_DELAY 100
@@ -134,7 +137,6 @@ static DEFINE_SPINLOCK(pci_link_down_lock);
#define FW_IMAGE_MISSION (0x02)
#define FW_IMAGE_BDATA (0x03)
#define FW_IMAGE_PRINT (0x04)
-#define FW_SETUP_DELAY 2000
#define SEG_METADATA (0x01)
#define SEG_NON_PAGED (0x02)
@@ -155,6 +157,7 @@ struct cnss_wlan_gpio_info {
};
struct cnss_wlan_vreg_info {
+ struct regulator *wlan_en_reg;
struct regulator *wlan_reg;
struct regulator *soc_swreg;
struct regulator *ant_switch;
@@ -237,6 +240,7 @@ static struct cnss_data {
dma_addr_t smmu_iova_start;
size_t smmu_iova_len;
struct cnss_wlan_vreg_info vreg_info;
+ bool wlan_en_vreg_support;
struct cnss_wlan_gpio_info gpio_info;
bool pcie_link_state;
bool pcie_link_down_ind;
@@ -274,10 +278,8 @@ static struct cnss_data {
u32 fw_dma_size;
u32 fw_seg_count;
struct segment_memory fw_seg_mem[MAX_NUM_OF_SEGMENTS];
- atomic_t fw_store_in_progress;
/* Firmware setup complete lock */
struct mutex fw_setup_stat_lock;
- struct completion fw_setup_complete;
void *bdata_cpu;
dma_addr_t bdata_dma;
u32 bdata_dma_size;
@@ -294,6 +296,17 @@ module_param(pcie_link_down_panic, uint, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(pcie_link_down_panic,
"Trigger kernel panic when PCIe link down is detected");
+static void cnss_put_wlan_enable_gpio(void)
+{
+ struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info;
+ struct cnss_wlan_vreg_info *vreg_info = &penv->vreg_info;
+
+ if (penv->wlan_en_vreg_support)
+ regulator_put(vreg_info->wlan_en_reg);
+ else
+ gpio_free(gpio_info->num);
+}
+
static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info)
{
int ret;
@@ -307,13 +320,6 @@ static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info)
}
}
- ret = regulator_enable(vreg_info->wlan_reg);
- if (ret) {
- pr_err("%s: regulator enable failed for WLAN power\n",
- __func__);
- goto error_enable;
- }
-
if (vreg_info->wlan_reg_io) {
ret = regulator_enable(vreg_info->wlan_reg_io);
if (ret) {
@@ -321,6 +327,8 @@ static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info)
__func__);
goto error_enable_reg_io;
}
+
+ usleep_range(WLAN_VREG_IO_DELAY_MIN, WLAN_VREG_IO_DELAY_MAX);
}
if (vreg_info->wlan_reg_xtal_aon) {
@@ -341,6 +349,13 @@ static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info)
}
}
+ ret = regulator_enable(vreg_info->wlan_reg);
+ if (ret) {
+ pr_err("%s: regulator enable failed for WLAN power\n",
+ __func__);
+ goto error_enable;
+ }
+
if (vreg_info->wlan_reg_sp2t) {
ret = regulator_enable(vreg_info->wlan_reg_sp2t);
if (ret) {
@@ -377,6 +392,8 @@ error_enable_ant_switch:
if (vreg_info->wlan_reg_sp2t)
regulator_disable(vreg_info->wlan_reg_sp2t);
error_enable_reg_sp2t:
+ regulator_disable(vreg_info->wlan_reg);
+error_enable:
if (vreg_info->wlan_reg_xtal)
regulator_disable(vreg_info->wlan_reg_xtal);
error_enable_reg_xtal:
@@ -386,8 +403,6 @@ error_enable_reg_xtal_aon:
if (vreg_info->wlan_reg_io)
regulator_disable(vreg_info->wlan_reg_io);
error_enable_reg_io:
- regulator_disable(vreg_info->wlan_reg);
-error_enable:
if (vreg_info->wlan_reg_core)
regulator_disable(vreg_info->wlan_reg_core);
error_enable_reg_core:
@@ -425,6 +440,13 @@ static int cnss_wlan_vreg_off(struct cnss_wlan_vreg_info *vreg_info)
}
}
+ ret = regulator_disable(vreg_info->wlan_reg);
+ if (ret) {
+ pr_err("%s: regulator disable failed for WLAN power\n",
+ __func__);
+ goto error_disable;
+ }
+
if (vreg_info->wlan_reg_xtal) {
ret = regulator_disable(vreg_info->wlan_reg_xtal);
if (ret) {
@@ -452,13 +474,6 @@ static int cnss_wlan_vreg_off(struct cnss_wlan_vreg_info *vreg_info)
}
}
- ret = regulator_disable(vreg_info->wlan_reg);
- if (ret) {
- pr_err("%s: regulator disable failed for WLAN power\n",
- __func__);
- goto error_disable;
- }
-
if (vreg_info->wlan_reg_core) {
ret = regulator_disable(vreg_info->wlan_reg_core);
if (ret) {
@@ -575,6 +590,25 @@ static void cnss_wlan_gpio_set(struct cnss_wlan_gpio_info *info, bool state)
info->name, info->state ? "enabled" : "disabled");
}
+static int cnss_configure_wlan_en_gpio(bool state)
+{
+ int ret = 0;
+ struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info;
+ struct cnss_wlan_vreg_info *vreg_info = &penv->vreg_info;
+
+ if (penv->wlan_en_vreg_support) {
+ if (state)
+ ret = regulator_enable(vreg_info->wlan_en_reg);
+ else
+ ret = regulator_disable(vreg_info->wlan_en_reg);
+ } else {
+ cnss_wlan_gpio_set(gpio_info, state);
+ }
+
+ msleep(WLAN_ENABLE_DELAY);
+ return ret;
+}
+
static int cnss_pinctrl_init(struct cnss_wlan_gpio_info *gpio_info,
struct platform_device *pdev)
{
@@ -681,14 +715,71 @@ end:
return ret;
}
+static int cnss_get_wlan_enable_gpio(
+ struct cnss_wlan_gpio_info *gpio_info,
+ struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device *dev = &pdev->dev;
+
+ if (!of_find_property(dev->of_node, gpio_info->name, NULL)) {
+ gpio_info->prop = false;
+ return -ENODEV;
+ }
+
+ gpio_info->prop = true;
+ ret = of_get_named_gpio(dev->of_node, gpio_info->name, 0);
+ if (ret >= 0) {
+ gpio_info->num = ret;
+ } else {
+ if (ret == -EPROBE_DEFER)
+ pr_debug("get WLAN_EN GPIO probe defer\n");
+ else
+ pr_err(
+ "can't get gpio %s ret %d", gpio_info->name, ret);
+ }
+
+ ret = cnss_pinctrl_init(gpio_info, pdev);
+ if (ret)
+ pr_debug("%s: pinctrl init failed!\n", __func__);
+
+ ret = cnss_wlan_gpio_init(gpio_info);
+ if (ret)
+ pr_err("gpio init failed\n");
+
+ return ret;
+}
+
+static int cnss_get_wlan_bootstrap_gpio(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device_node *node = (&pdev->dev)->of_node;
+
+ if (!of_find_property(node, WLAN_BOOTSTRAP_GPIO_NAME, NULL))
+ return ret;
+
+ penv->wlan_bootstrap_gpio =
+ of_get_named_gpio(node, WLAN_BOOTSTRAP_GPIO_NAME, 0);
+ if (penv->wlan_bootstrap_gpio > 0) {
+ ret = cnss_wlan_bootstrap_gpio_init();
+ } else {
+ ret = penv->wlan_bootstrap_gpio;
+ pr_err(
+ "%s: Can't get GPIO %s, ret = %d",
+ __func__, WLAN_BOOTSTRAP_GPIO_NAME, ret);
+ }
+
+ return ret;
+}
+
static int cnss_wlan_get_resources(struct platform_device *pdev)
{
int ret = 0;
struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info;
struct cnss_wlan_vreg_info *vreg_info = &penv->vreg_info;
+ struct device_node *node = pdev->dev.of_node;
- if (of_get_property(pdev->dev.of_node,
- WLAN_VREG_CORE_NAME"-supply", NULL)) {
+ if (of_get_property(node, WLAN_VREG_CORE_NAME "-supply", NULL)) {
vreg_info->wlan_reg_core = regulator_get(&pdev->dev,
WLAN_VREG_CORE_NAME);
if (IS_ERR(vreg_info->wlan_reg_core)) {
@@ -718,26 +809,7 @@ static int cnss_wlan_get_resources(struct platform_device *pdev)
}
}
- vreg_info->wlan_reg = regulator_get(&pdev->dev, WLAN_VREG_NAME);
-
- if (IS_ERR(vreg_info->wlan_reg)) {
- if (PTR_ERR(vreg_info->wlan_reg) == -EPROBE_DEFER)
- pr_err("%s: vreg probe defer\n", __func__);
- else
- pr_err("%s: vreg regulator get failed\n", __func__);
- ret = PTR_ERR(vreg_info->wlan_reg);
- goto err_reg_get;
- }
-
- ret = regulator_enable(vreg_info->wlan_reg);
-
- if (ret) {
- pr_err("%s: vreg initial vote failed\n", __func__);
- goto err_reg_enable;
- }
-
- if (of_get_property(pdev->dev.of_node,
- WLAN_VREG_IO_NAME"-supply", NULL)) {
+ if (of_get_property(node, WLAN_VREG_IO_NAME "-supply", NULL)) {
vreg_info->wlan_reg_io = regulator_get(&pdev->dev,
WLAN_VREG_IO_NAME);
if (!IS_ERR(vreg_info->wlan_reg_io)) {
@@ -755,14 +827,34 @@ static int cnss_wlan_get_resources(struct platform_device *pdev)
__func__);
goto err_reg_io_enable;
}
+
+ usleep_range(WLAN_VREG_IO_DELAY_MIN,
+ WLAN_VREG_IO_DELAY_MAX);
}
}
if (cnss_enable_xtal_ldo(pdev))
goto err_reg_xtal_enable;
- if (of_get_property(pdev->dev.of_node,
- WLAN_VREG_SP2T_NAME"-supply", NULL)) {
+ vreg_info->wlan_reg = regulator_get(&pdev->dev, WLAN_VREG_NAME);
+
+ if (IS_ERR(vreg_info->wlan_reg)) {
+ if (PTR_ERR(vreg_info->wlan_reg) == -EPROBE_DEFER)
+ pr_err("%s: vreg probe defer\n", __func__);
+ else
+ pr_err("%s: vreg regulator get failed\n", __func__);
+ ret = PTR_ERR(vreg_info->wlan_reg);
+ goto err_reg_get;
+ }
+
+ ret = regulator_enable(vreg_info->wlan_reg);
+
+ if (ret) {
+ pr_err("%s: vreg initial vote failed\n", __func__);
+ goto err_reg_enable;
+ }
+
+ if (of_get_property(node, WLAN_VREG_SP2T_NAME "-supply", NULL)) {
vreg_info->wlan_reg_sp2t =
regulator_get(&pdev->dev, WLAN_VREG_SP2T_NAME);
if (!IS_ERR(vreg_info->wlan_reg_sp2t)) {
@@ -783,8 +875,7 @@ static int cnss_wlan_get_resources(struct platform_device *pdev)
}
}
- if (of_get_property(pdev->dev.of_node,
- WLAN_ANT_SWITCH_NAME "-supply", NULL)) {
+ if (of_get_property(node, WLAN_ANT_SWITCH_NAME "-supply", NULL)) {
vreg_info->ant_switch =
regulator_get(&pdev->dev, WLAN_ANT_SWITCH_NAME);
if (!IS_ERR(vreg_info->ant_switch)) {
@@ -814,13 +905,10 @@ static int cnss_wlan_get_resources(struct platform_device *pdev)
}
}
- if (of_find_property((&pdev->dev)->of_node,
- "qcom,wlan-uart-access", NULL))
+ if (of_find_property(node, "qcom,wlan-uart-access", NULL))
penv->cap.cap_flag |= CNSS_HAS_UART_ACCESS;
- if (of_get_property(pdev->dev.of_node,
- WLAN_SWREG_NAME"-supply", NULL)) {
-
+ if (of_get_property(node, WLAN_SWREG_NAME "-supply", NULL)) {
vreg_info->soc_swreg = regulator_get(&pdev->dev,
WLAN_SWREG_NAME);
if (IS_ERR(vreg_info->soc_swreg)) {
@@ -843,68 +931,41 @@ static int cnss_wlan_get_resources(struct platform_device *pdev)
penv->cap.cap_flag |= CNSS_HAS_EXTERNAL_SWREG;
}
- vreg_info->state = VREG_ON;
-
- if (!of_find_property((&pdev->dev)->of_node, gpio_info->name, NULL)) {
- gpio_info->prop = false;
- goto end;
+ penv->wlan_en_vreg_support =
+ of_property_read_bool(node, "qcom,wlan-en-vreg-support");
+ if (penv->wlan_en_vreg_support) {
+ vreg_info->wlan_en_reg =
+ regulator_get(&pdev->dev, WLAN_EN_VREG_NAME);
+ if (IS_ERR(vreg_info->wlan_en_reg)) {
+ pr_err("%s:wlan_en vreg get failed\n", __func__);
+ ret = PTR_ERR(vreg_info->wlan_en_reg);
+ goto err_wlan_en_reg_get;
+ }
}
- gpio_info->prop = true;
- ret = of_get_named_gpio((&pdev->dev)->of_node,
- gpio_info->name, 0);
-
- if (ret >= 0) {
- gpio_info->num = ret;
- ret = 0;
- } else {
- if (ret == -EPROBE_DEFER)
- pr_debug("get WLAN_EN GPIO probe defer\n");
- else
- pr_err("can't get gpio %s ret %d",
- gpio_info->name, ret);
- goto err_get_gpio;
+ if (!penv->wlan_en_vreg_support) {
+ ret = cnss_get_wlan_enable_gpio(gpio_info, pdev);
+ if (ret) {
+ pr_err(
+ "%s:Failed to config the WLAN_EN gpio\n", __func__);
+ goto err_gpio_wlan_en;
+ }
}
+ vreg_info->state = VREG_ON;
- ret = cnss_pinctrl_init(gpio_info, pdev);
+ ret = cnss_get_wlan_bootstrap_gpio(pdev);
if (ret) {
- pr_err("%s: pinctrl init failed!\n", __func__);
- goto err_pinctrl_init;
+ pr_err("%s: Failed to enable wlan bootstrap gpio\n", __func__);
+ goto err_gpio_wlan_bootstrap;
}
- ret = cnss_wlan_gpio_init(gpio_info);
- if (ret) {
- pr_err("gpio init failed\n");
- goto err_gpio_init;
- }
-
- if (of_find_property((&pdev->dev)->of_node,
- WLAN_BOOTSTRAP_GPIO_NAME, NULL)) {
- penv->wlan_bootstrap_gpio =
- of_get_named_gpio((&pdev->dev)->of_node,
- WLAN_BOOTSTRAP_GPIO_NAME, 0);
- if (penv->wlan_bootstrap_gpio > 0) {
- ret = cnss_wlan_bootstrap_gpio_init();
- if (ret)
- goto err_gpio_init;
- } else {
- if (ret == -EPROBE_DEFER) {
- pr_debug("%s: Get GPIO %s probe defer\n",
- __func__, WLAN_BOOTSTRAP_GPIO_NAME);
- } else {
- pr_err("%s: Can't get GPIO %s, ret = %d",
- __func__, WLAN_BOOTSTRAP_GPIO_NAME,
- ret);
- }
- goto err_gpio_init;
- }
- }
-end:
return ret;
-err_gpio_init:
-err_pinctrl_init:
-err_get_gpio:
+err_gpio_wlan_bootstrap:
+ cnss_put_wlan_enable_gpio();
+err_gpio_wlan_en:
+err_wlan_en_reg_get:
+ vreg_info->wlan_en_reg = NULL;
if (vreg_info->soc_swreg)
regulator_disable(vreg_info->soc_swreg);
vreg_info->state = VREG_OFF;
@@ -929,7 +990,11 @@ err_reg_sp2t_enable:
err_reg_sp2t_set:
if (vreg_info->wlan_reg_sp2t)
regulator_put(vreg_info->wlan_reg_sp2t);
+ regulator_disable(vreg_info->wlan_reg);
+err_reg_enable:
+ regulator_put(vreg_info->wlan_reg);
+err_reg_get:
cnss_disable_xtal_ldo(pdev);
err_reg_xtal_enable:
@@ -940,12 +1005,6 @@ err_reg_io_enable:
err_reg_io_set:
if (vreg_info->wlan_reg_io)
regulator_put(vreg_info->wlan_reg_io);
- regulator_disable(vreg_info->wlan_reg);
-
-err_reg_enable:
- regulator_put(vreg_info->wlan_reg);
-
-err_reg_get:
if (vreg_info->wlan_reg_core)
regulator_disable(vreg_info->wlan_reg_core);
@@ -965,7 +1024,7 @@ static void cnss_wlan_release_resources(void)
if (penv->wlan_bootstrap_gpio > 0)
gpio_free(penv->wlan_bootstrap_gpio);
- gpio_free(gpio_info->num);
+ cnss_put_wlan_enable_gpio();
gpio_info->state = WLAN_EN_LOW;
gpio_info->prop = false;
cnss_wlan_vreg_set(vreg_info, VREG_OFF);
@@ -975,13 +1034,13 @@ static void cnss_wlan_release_resources(void)
regulator_put(vreg_info->ant_switch);
if (vreg_info->wlan_reg_sp2t)
regulator_put(vreg_info->wlan_reg_sp2t);
+ regulator_put(vreg_info->wlan_reg);
if (vreg_info->wlan_reg_xtal)
regulator_put(vreg_info->wlan_reg_xtal);
if (vreg_info->wlan_reg_xtal_aon)
regulator_put(vreg_info->wlan_reg_xtal_aon);
if (vreg_info->wlan_reg_io)
regulator_put(vreg_info->wlan_reg_io);
- regulator_put(vreg_info->wlan_reg);
if (vreg_info->wlan_reg_core)
regulator_put(vreg_info->wlan_reg_core);
vreg_info->state = VREG_OFF;
@@ -1374,15 +1433,6 @@ int cnss_get_fw_image(struct image_desc_info *image_desc_info)
!penv->fw_seg_count || !penv->bdata_seg_count)
return -EINVAL;
- /* Check for firmware setup trigger by usersapce is in progress
- * and wait for complition of firmware setup.
- */
-
- if (atomic_read(&penv->fw_store_in_progress)) {
- wait_for_completion_timeout(&penv->fw_setup_complete,
- msecs_to_jiffies(FW_SETUP_DELAY));
- }
-
mutex_lock(&penv->fw_setup_stat_lock);
image_desc_info->fw_addr = penv->fw_dma;
image_desc_info->fw_size = penv->fw_dma_size;
@@ -1560,7 +1610,6 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev,
{
int ret = 0;
struct cnss_wlan_vreg_info *vreg_info = &penv->vreg_info;
- struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info;
void *cpu_addr;
dma_addr_t dma_handle;
struct codeswap_codeseg_info *cnss_seg_info = NULL;
@@ -1619,7 +1668,7 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev,
penv->pcie_link_state = PCIE_LINK_DOWN;
}
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
+ cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
ret = cnss_wlan_vreg_set(vreg_info, VREG_OFF);
if (ret) {
@@ -1627,7 +1676,9 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev,
goto err_pcie_suspend;
}
+ mutex_lock(&penv->fw_setup_stat_lock);
cnss_wlan_fw_mem_alloc(pdev);
+ mutex_unlock(&penv->fw_setup_stat_lock);
ret = device_create_file(&penv->pldev->dev, &dev_attr_wlan_setup);
@@ -1874,17 +1925,11 @@ static ssize_t fw_image_setup_store(struct device *dev,
if (!penv)
return -ENODEV;
- if (atomic_read(&penv->fw_store_in_progress)) {
- pr_info("%s: Firmware setup in progress\n", __func__);
- return 0;
- }
-
- atomic_set(&penv->fw_store_in_progress, 1);
- init_completion(&penv->fw_setup_complete);
+ mutex_lock(&penv->fw_setup_stat_lock);
+ pr_info("%s: Firmware setup in progress\n", __func__);
if (kstrtoint(buf, 0, &val)) {
- atomic_set(&penv->fw_store_in_progress, 0);
- complete(&penv->fw_setup_complete);
+ mutex_unlock(&penv->fw_setup_stat_lock);
return -EINVAL;
}
@@ -1895,8 +1940,7 @@ static ssize_t fw_image_setup_store(struct device *dev,
if (ret != 0) {
pr_err("%s: Invalid parsing of FW image files %d",
__func__, ret);
- atomic_set(&penv->fw_store_in_progress, 0);
- complete(&penv->fw_setup_complete);
+ mutex_unlock(&penv->fw_setup_stat_lock);
return -EINVAL;
}
penv->fw_image_setup = val;
@@ -1906,9 +1950,8 @@ static ssize_t fw_image_setup_store(struct device *dev,
penv->bmi_test = val;
}
- atomic_set(&penv->fw_store_in_progress, 0);
- complete(&penv->fw_setup_complete);
-
+ pr_info("%s: Firmware setup completed\n", __func__);
+ mutex_unlock(&penv->fw_setup_stat_lock);
return count;
}
@@ -2007,16 +2050,21 @@ int cnss_get_codeswap_struct(struct codeswap_codeseg_info *swap_seg)
{
struct codeswap_codeseg_info *cnss_seg_info = penv->cnss_seg_info;
+ mutex_lock(&penv->fw_setup_stat_lock);
if (!cnss_seg_info) {
swap_seg = NULL;
+ mutex_unlock(&penv->fw_setup_stat_lock);
return -ENOENT;
}
+
if (!atomic_read(&penv->fw_available)) {
pr_debug("%s: fw is not available\n", __func__);
+ mutex_unlock(&penv->fw_setup_stat_lock);
return -ENOENT;
}
*swap_seg = *cnss_seg_info;
+ mutex_unlock(&penv->fw_setup_stat_lock);
return 0;
}
@@ -2035,15 +2083,6 @@ static void cnss_wlan_memory_expansion(void)
u_int32_t total_length = 0;
struct pci_dev *pdev;
- /* Check for firmware setup trigger by usersapce is in progress
- * and wait for complition of firmware setup.
- */
-
- if (atomic_read(&penv->fw_store_in_progress)) {
- wait_for_completion_timeout(&penv->fw_setup_complete,
- msecs_to_jiffies(FW_SETUP_DELAY));
- }
-
mutex_lock(&penv->fw_setup_stat_lock);
filename = cnss_wlan_get_evicted_data_file();
pdev = penv->pdev;
@@ -2276,8 +2315,7 @@ again:
msleep(WLAN_BOOTSTRAP_DELAY);
}
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_HIGH);
- msleep(WLAN_ENABLE_DELAY);
+ cnss_configure_wlan_en_gpio(WLAN_EN_HIGH);
if (!pdev) {
pr_debug("%s: invalid pdev. register pci device\n", __func__);
@@ -2360,8 +2398,7 @@ again:
cnss_get_pci_dev_bus_number(pdev),
pdev, PM_OPTIONS);
penv->pcie_link_state = PCIE_LINK_DOWN;
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
- msleep(WLAN_ENABLE_DELAY);
+ cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
cnss_wlan_vreg_set(vreg_info, VREG_OFF);
msleep(POWER_ON_DELAY);
probe_again++;
@@ -2388,7 +2425,7 @@ err_pcie_link_up:
}
err_pcie_reg:
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
+ cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
cnss_wlan_vreg_set(vreg_info, VREG_OFF);
if (penv->pdev) {
pr_err("%d: Unregistering PCI device\n", __LINE__);
@@ -2469,8 +2506,7 @@ void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver)
cut_power:
penv->driver = NULL;
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
-
+ cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
if (cnss_wlan_vreg_set(vreg_info, VREG_OFF))
pr_err("wlan vreg OFF failed\n");
}
@@ -2582,8 +2618,7 @@ static int cnss_shutdown(const struct subsys_desc *subsys, bool force_stop)
}
cut_power:
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
-
+ cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
if (cnss_wlan_vreg_set(vreg_info, VREG_OFF))
pr_err("cnss: Failed to set WLAN VREG_OFF!\n");
@@ -2616,8 +2651,7 @@ static int cnss_powerup(const struct subsys_desc *subsys)
}
msleep(POWER_ON_DELAY);
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_HIGH);
- msleep(WLAN_ENABLE_DELAY);
+ cnss_configure_wlan_en_gpio(WLAN_EN_HIGH);
if (!pdev) {
pr_err("%d: invalid pdev\n", __LINE__);
@@ -2677,7 +2711,7 @@ err_wlan_reinit:
penv->pcie_link_state = PCIE_LINK_DOWN;
err_pcie_link_up:
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
+ cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
cnss_wlan_vreg_set(vreg_info, VREG_OFF);
if (penv->pdev) {
pr_err("%d: Unregistering pci device\n", __LINE__);
@@ -2859,13 +2893,17 @@ static int cnss_probe(struct platform_device *pdev)
penv->vreg_info.wlan_reg = NULL;
penv->vreg_info.state = VREG_OFF;
penv->pci_register_again = false;
+ mutex_init(&penv->fw_setup_stat_lock);
ret = cnss_wlan_get_resources(pdev);
if (ret)
goto err_get_wlan_res;
- cnss_wlan_gpio_set(&penv->gpio_info, WLAN_EN_HIGH);
- msleep(WLAN_ENABLE_DELAY);
+ ret = cnss_configure_wlan_en_gpio(WLAN_EN_HIGH);
+ if (ret) {
+ pr_err("%s: Failed to enable WLAN enable gpio\n", __func__);
+ goto err_get_rc;
+ }
ret = of_property_read_u32(dev->of_node, "qcom,wlan-rc-num", &rc_num);
if (ret) {
@@ -3016,8 +3054,6 @@ skip_ramdump:
memset(phys_to_virt(0), 0, SZ_4K);
#endif
- atomic_set(&penv->fw_store_in_progress, 0);
- mutex_init(&penv->fw_setup_stat_lock);
ret = device_create_file(dev, &dev_attr_fw_image_setup);
if (ret) {
pr_err("cnss: fw_image_setup sys file creation failed\n");
@@ -3062,7 +3098,7 @@ err_subsys_reg:
err_esoc_reg:
err_pcie_enumerate:
err_get_rc:
- cnss_wlan_gpio_set(&penv->gpio_info, WLAN_EN_LOW);
+ cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
cnss_wlan_release_resources();
err_get_wlan_res:
@@ -3073,8 +3109,6 @@ err_get_wlan_res:
static int cnss_remove(struct platform_device *pdev)
{
- struct cnss_wlan_gpio_info *gpio_info = &penv->gpio_info;
-
unregister_pm_notifier(&cnss_pm_notifier);
device_remove_file(&pdev->dev, &dev_attr_fw_image_setup);
@@ -3095,7 +3129,7 @@ static int cnss_remove(struct platform_device *pdev)
}
}
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
+ cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
if (penv->wlan_bootstrap_gpio > 0)
gpio_set_value(penv->wlan_bootstrap_gpio, WLAN_BOOTSTRAP_LOW);
cnss_wlan_release_resources();
@@ -3596,8 +3630,7 @@ static int __cnss_pcie_power_up(struct device *dev)
msleep(WLAN_BOOTSTRAP_DELAY);
}
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_HIGH);
- msleep(WLAN_ENABLE_DELAY);
+ cnss_configure_wlan_en_gpio(WLAN_EN_HIGH);
return 0;
}
@@ -3610,8 +3643,7 @@ static int __cnss_pcie_power_down(struct device *dev)
vreg_info = &penv->vreg_info;
gpio_info = &penv->gpio_info;
- cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
-
+ cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
if (penv->wlan_bootstrap_gpio > 0)
gpio_set_value(penv->wlan_bootstrap_gpio, WLAN_BOOTSTRAP_LOW);
diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c
index ce7dbc64c4c3..8586bb16cfd3 100644
--- a/drivers/net/wireless/cnss/cnss_sdio.c
+++ b/drivers/net/wireless/cnss/cnss_sdio.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -276,6 +276,11 @@ static int cnss_get_hw_resources(struct device *dev)
host = info->host;
+ if (!host) {
+ pr_err("%s: MMC host is invalid\n", __func__);
+ return -ENODEV;
+ }
+
ret = regulator_enable(cnss_pdata->regulator.wlan_vreg);
if (ret) {
pr_err("%s: Failed to enable wlan vreg\n", __func__);
diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c
index 360ab31c61dd..35d7fe1c318c 100644
--- a/drivers/net/wireless/cnss2/debug.c
+++ b/drivers/net/wireless/cnss2/debug.c
@@ -15,6 +15,7 @@
#include <linux/debugfs.h>
#include "main.h"
#include "debug.h"
+#include "pci.h"
#define CNSS_IPC_LOG_PAGES 32
@@ -97,6 +98,10 @@ static int cnss_stats_show_state(struct seq_file *s,
continue;
case CNSS_DEV_ERR_NOTIFY:
seq_puts(s, "DEV_ERR");
+ continue;
+ case CNSS_DRIVER_DEBUG:
+ seq_puts(s, "DRIVER_DEBUG");
+ continue;
}
seq_printf(s, "UNKNOWN-%d", i);
@@ -121,13 +126,323 @@ static int cnss_stats_open(struct inode *inode, struct file *file)
}
static const struct file_operations cnss_stats_fops = {
- .read = seq_read,
- .release = single_release,
- .open = cnss_stats_open,
- .owner = THIS_MODULE,
- .llseek = seq_lseek,
+ .read = seq_read,
+ .release = single_release,
+ .open = cnss_stats_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
+static ssize_t cnss_dev_boot_debug_write(struct file *fp,
+ const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct cnss_plat_data *plat_priv =
+ ((struct seq_file *)fp->private_data)->private;
+ char buf[64];
+ char *cmd;
+ unsigned int len = 0;
+ int ret = 0;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ cmd = buf;
+
+ if (sysfs_streq(cmd, "on")) {
+ ret = cnss_power_on_device(plat_priv);
+ } else if (sysfs_streq(cmd, "off")) {
+ cnss_power_off_device(plat_priv);
+ } else if (sysfs_streq(cmd, "enumerate")) {
+ ret = cnss_pci_init(plat_priv);
+ } else if (sysfs_streq(cmd, "download")) {
+ ret = cnss_pci_start_mhi(plat_priv->bus_priv);
+ } else if (sysfs_streq(cmd, "linkup")) {
+ ret = cnss_resume_pci_link(plat_priv->bus_priv);
+ } else if (sysfs_streq(cmd, "linkdown")) {
+ ret = cnss_suspend_pci_link(plat_priv->bus_priv);
+ } else if (sysfs_streq(cmd, "powerup")) {
+ set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
+ ret = cnss_driver_event_post(plat_priv,
+ CNSS_DRIVER_EVENT_POWER_UP,
+ true, NULL);
+ } else if (sysfs_streq(cmd, "shutdown")) {
+ ret = cnss_driver_event_post(plat_priv,
+ CNSS_DRIVER_EVENT_POWER_DOWN,
+ true, NULL);
+ clear_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
+ } else {
+ cnss_pr_err("Device boot debugfs command is invalid\n");
+ ret = -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static int cnss_dev_boot_debug_show(struct seq_file *s, void *data)
+{
+ seq_puts(s, "\nUsage: echo <action> > <debugfs_path>/cnss/dev_boot\n");
+ seq_puts(s, "<action> can be one of below:\n");
+ seq_puts(s, "on: turn on device power, assert WLAN_EN\n");
+ seq_puts(s, "off: de-assert WLAN_EN, turn off device power\n");
+ seq_puts(s, "enumerate: de-assert PERST, enumerate PCIe\n");
+ seq_puts(s, "download: download FW and do QMI handshake with FW\n");
+ seq_puts(s, "linkup: bring up PCIe link\n");
+ seq_puts(s, "linkdown: bring down PCIe link\n");
+ seq_puts(s, "powerup: full power on sequence to boot device, download FW and do QMI handshake with FW\n");
+ seq_puts(s, "shutdown: full power off sequence to shutdown device\n");
+
+ return 0;
+}
+
+static int cnss_dev_boot_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cnss_dev_boot_debug_show, inode->i_private);
+}
+
+static const struct file_operations cnss_dev_boot_debug_fops = {
+ .read = seq_read,
+ .write = cnss_dev_boot_debug_write,
+ .release = single_release,
+ .open = cnss_dev_boot_debug_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
+static int cnss_reg_read_debug_show(struct seq_file *s, void *data)
+{
+ struct cnss_plat_data *plat_priv = s->private;
+
+ mutex_lock(&plat_priv->dev_lock);
+ if (!plat_priv->diag_reg_read_buf) {
+ seq_puts(s, "\nUsage: echo <mem_type> <offset> <data_len> > <debugfs_path>/cnss/reg_read\n");
+ mutex_unlock(&plat_priv->dev_lock);
+ return 0;
+ }
+
+ seq_printf(s, "\nRegister read, address: 0x%x memory type: 0x%x length: 0x%x\n\n",
+ plat_priv->diag_reg_read_addr,
+ plat_priv->diag_reg_read_mem_type,
+ plat_priv->diag_reg_read_len);
+
+ seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4,
+ plat_priv->diag_reg_read_buf,
+ plat_priv->diag_reg_read_len, false);
+
+ plat_priv->diag_reg_read_len = 0;
+ kfree(plat_priv->diag_reg_read_buf);
+ plat_priv->diag_reg_read_buf = NULL;
+ mutex_unlock(&plat_priv->dev_lock);
+
+ return 0;
+}
+
+static ssize_t cnss_reg_read_debug_write(struct file *fp,
+ const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct cnss_plat_data *plat_priv =
+ ((struct seq_file *)fp->private_data)->private;
+ char buf[64];
+ char *sptr, *token;
+ unsigned int len = 0;
+ u32 reg_offset, mem_type;
+ u32 data_len = 0;
+ u8 *reg_buf = NULL;
+ const char *delim = " ";
+ int ret = 0;
+
+ if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) {
+ cnss_pr_err("Firmware is not ready yet\n");
+ return -EINVAL;
+ }
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &mem_type))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_offset))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &data_len))
+ return -EINVAL;
+
+ if (data_len == 0 ||
+ data_len > QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01)
+ return -EINVAL;
+
+ mutex_lock(&plat_priv->dev_lock);
+ kfree(plat_priv->diag_reg_read_buf);
+ plat_priv->diag_reg_read_buf = NULL;
+
+ reg_buf = kzalloc(data_len, GFP_KERNEL);
+ if (!reg_buf) {
+ mutex_unlock(&plat_priv->dev_lock);
+ return -ENOMEM;
+ }
+
+ ret = cnss_wlfw_athdiag_read_send_sync(plat_priv, reg_offset,
+ mem_type, data_len,
+ reg_buf);
+ if (ret) {
+ kfree(reg_buf);
+ mutex_unlock(&plat_priv->dev_lock);
+ return ret;
+ }
+
+ plat_priv->diag_reg_read_addr = reg_offset;
+ plat_priv->diag_reg_read_mem_type = mem_type;
+ plat_priv->diag_reg_read_len = data_len;
+ plat_priv->diag_reg_read_buf = reg_buf;
+ mutex_unlock(&plat_priv->dev_lock);
+
+ return count;
+}
+
+static int cnss_reg_read_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cnss_reg_read_debug_show, inode->i_private);
+}
+
+static const struct file_operations cnss_reg_read_debug_fops = {
+ .read = seq_read,
+ .write = cnss_reg_read_debug_write,
+ .open = cnss_reg_read_debug_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
+static int cnss_reg_write_debug_show(struct seq_file *s, void *data)
+{
+ seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs_path>/cnss/reg_write\n");
+
+ return 0;
+}
+
+static ssize_t cnss_reg_write_debug_write(struct file *fp,
+ const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct cnss_plat_data *plat_priv =
+ ((struct seq_file *)fp->private_data)->private;
+ char buf[64];
+ char *sptr, *token;
+ unsigned int len = 0;
+ u32 reg_offset, mem_type, reg_val;
+ const char *delim = " ";
+ int ret = 0;
+
+ if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) {
+ cnss_pr_err("Firmware is not ready yet\n");
+ return -EINVAL;
+ }
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &mem_type))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_offset))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_val))
+ return -EINVAL;
+
+ ret = cnss_wlfw_athdiag_write_send_sync(plat_priv, reg_offset, mem_type,
+ sizeof(u32),
+ (u8 *)&reg_val);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static int cnss_reg_write_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cnss_reg_write_debug_show, inode->i_private);
+}
+
+static const struct file_operations cnss_reg_write_debug_fops = {
+ .read = seq_read,
+ .write = cnss_reg_write_debug_write,
+ .open = cnss_reg_write_debug_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
};
+#ifdef CONFIG_CNSS2_DEBUG
+static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv)
+{
+ struct dentry *root_dentry = plat_priv->root_dentry;
+
+ debugfs_create_file("dev_boot", 0600, root_dentry, plat_priv,
+ &cnss_dev_boot_debug_fops);
+ debugfs_create_file("reg_read", 0600, root_dentry, plat_priv,
+ &cnss_reg_read_debug_fops);
+ debugfs_create_file("reg_write", 0600, root_dentry, plat_priv,
+ &cnss_reg_write_debug_fops);
+
+ return 0;
+}
+#else
+static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv)
+{
+ return 0;
+}
+#endif
+
int cnss_debugfs_create(struct cnss_plat_data *plat_priv)
{
int ret = 0;
@@ -139,11 +454,16 @@ int cnss_debugfs_create(struct cnss_plat_data *plat_priv)
cnss_pr_err("Unable to create debugfs %d\n", ret);
goto out;
}
+
plat_priv->root_dentry = root_dentry;
+
debugfs_create_file("pin_connect_result", 0644, root_dentry, plat_priv,
&cnss_pin_connect_fops);
debugfs_create_file("stats", 0644, root_dentry, plat_priv,
&cnss_stats_fops);
+
+ cnss_create_debug_only_node(plat_priv);
+
out:
return ret;
}
diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c
index e114d0c51a07..23a81ff071ee 100644
--- a/drivers/net/wireless/cnss2/main.c
+++ b/drivers/net/wireless/cnss2/main.c
@@ -57,6 +57,7 @@ MODULE_PARM_DESC(enable_waltest, "Enable to handle firmware waltest");
enum cnss_debug_quirks {
LINK_DOWN_SELF_RECOVERY,
+ SKIP_DEVICE_BOOT,
};
unsigned long quirks;
@@ -1059,11 +1060,15 @@ static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv)
if (!pci_priv)
return -ENODEV;
+ if (test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state))
+ goto skip_driver_remove;
+
if (!plat_priv->driver_ops)
return -EINVAL;
cnss_driver_call_remove(plat_priv);
+skip_driver_remove:
cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE);
cnss_pci_set_monitor_wake_intr(pci_priv, false);
cnss_pci_set_auto_suspended(pci_priv, 0);
@@ -1161,7 +1166,8 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv)
return -ENODEV;
if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) ||
- test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state))
+ test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) ||
+ test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state))
goto skip_driver_remove;
if (!plat_priv->driver_ops)
@@ -2232,13 +2238,15 @@ static int cnss_probe(struct platform_device *plat_dev)
if (ret)
goto reset_ctx;
- ret = cnss_power_on_device(plat_priv);
- if (ret)
- goto free_res;
+ if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) {
+ ret = cnss_power_on_device(plat_priv);
+ if (ret)
+ goto free_res;
- ret = cnss_pci_init(plat_priv);
- if (ret)
- goto power_off;
+ ret = cnss_pci_init(plat_priv);
+ if (ret)
+ goto power_off;
+ }
ret = cnss_register_esoc(plat_priv);
if (ret)
@@ -2275,6 +2283,7 @@ static int cnss_probe(struct platform_device *plat_dev)
ret);
init_completion(&plat_priv->power_up_complete);
+ mutex_init(&plat_priv->dev_lock);
cnss_pr_info("Platform driver probed successfully.\n");
@@ -2291,9 +2300,11 @@ unreg_bus_scale:
unreg_esoc:
cnss_unregister_esoc(plat_priv);
deinit_pci:
- cnss_pci_deinit(plat_priv);
+ if (!test_bit(SKIP_DEVICE_BOOT, &quirks))
+ cnss_pci_deinit(plat_priv);
power_off:
- cnss_power_off_device(plat_priv);
+ if (!test_bit(SKIP_DEVICE_BOOT, &quirks))
+ cnss_power_off_device(plat_priv);
free_res:
cnss_put_resources(plat_priv);
reset_ctx:
diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h
index a2d9a02bde20..a5f9ce37b0ea 100644
--- a/drivers/net/wireless/cnss2/main.h
+++ b/drivers/net/wireless/cnss2/main.h
@@ -142,6 +142,7 @@ enum cnss_driver_state {
CNSS_DRIVER_RECOVERY,
CNSS_FW_BOOT_RECOVERY,
CNSS_DEV_ERR_NOTIFY,
+ CNSS_DRIVER_DEBUG,
};
struct cnss_recovery_data {
@@ -204,6 +205,11 @@ struct cnss_plat_data {
atomic_t pm_count;
struct timer_list fw_boot_timer;
struct completion power_up_complete;
+ struct mutex dev_lock; /* mutex for register access through debugfs */
+ u32 diag_reg_read_addr;
+ u32 diag_reg_read_mem_type;
+ u32 diag_reg_read_len;
+ u8 *diag_reg_read_buf;
};
void *cnss_bus_dev_to_bus_priv(struct device *dev);
diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c
index edc39af2d361..2c297fba5c34 100644
--- a/drivers/net/wireless/cnss2/pci.c
+++ b/drivers/net/wireless/cnss2/pci.c
@@ -787,36 +787,6 @@ int cnss_pci_get_bar_info(struct cnss_pci_data *pci_priv, void __iomem **va,
return 0;
}
-#ifdef CONFIG_CNSS_QCA6290
-#define PCI_MAX_BAR_SIZE 0xD00000
-
-static void __iomem *cnss_pci_iomap(struct pci_dev *dev, int bar,
- unsigned long maxlen)
-{
- resource_size_t start = pci_resource_start(dev, bar);
- resource_size_t len = PCI_MAX_BAR_SIZE;
- unsigned long flags = pci_resource_flags(dev, bar);
-
- if (!len || !start)
- return NULL;
-
- if ((flags & IORESOURCE_IO) || (flags & IORESOURCE_MEM)) {
- if (flags & IORESOURCE_CACHEABLE && !(flags & IORESOURCE_IO))
- return ioremap(start, len);
- else
- return ioremap_nocache(start, len);
- }
-
- return NULL;
-}
-#else
-static void __iomem *cnss_pci_iomap(struct pci_dev *dev, int bar,
- unsigned long maxlen)
-{
- return pci_iomap(dev, bar, maxlen);
-}
-#endif
-
static struct cnss_msi_config msi_config = {
.total_vectors = 32,
.total_users = 4,
@@ -1003,7 +973,7 @@ static int cnss_pci_enable_bus(struct cnss_pci_data *pci_priv)
pci_set_master(pci_dev);
- pci_priv->bar = cnss_pci_iomap(pci_dev, PCI_BAR_NUM, 0);
+ pci_priv->bar = pci_iomap(pci_dev, PCI_BAR_NUM, 0);
if (!pci_priv->bar) {
cnss_pr_err("Failed to do PCI IO map!\n");
ret = -EIO;
@@ -1493,6 +1463,11 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
cnss_pci_disable_msi(pci_priv);
goto disable_bus;
}
+ ret = cnss_suspend_pci_link(pci_priv);
+ if (ret)
+ cnss_pr_err("Failed to suspend PCI link, err = %d\n",
+ ret);
+ cnss_power_off_device(plat_priv);
break;
default:
cnss_pr_err("Unknown PCI device found: 0x%x\n",
diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c
index d1c0423b4517..e010e2c39f02 100644
--- a/drivers/net/wireless/cnss2/qmi.c
+++ b/drivers/net/wireless/cnss2/qmi.c
@@ -39,7 +39,7 @@ static bool daemon_support;
module_param(daemon_support, bool, 0600);
MODULE_PARM_DESC(daemon_support, "User space has cnss-daemon support or not");
-static bool bdf_bypass = true;
+static bool bdf_bypass;
#ifdef CONFIG_CNSS2_DEBUG
module_param(bdf_bypass, bool, 0600);
MODULE_PARM_DESC(bdf_bypass, "If BDF is not found, send dummy BDF to FW");
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/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index d59769e858f4..019d7165a045 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2539,7 +2539,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
tasklet_hrtimer_init(&data->beacon_timer,
mac80211_hwsim_beacon,
- CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS);
+ CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
spin_lock_bh(&hwsim_radio_lock);
list_add_tail(&data->list, &hwsim_radios);
diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 44f059f7f34e..9ebe00ea8f81 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -71,7 +71,7 @@
* only support SPI for 12xx - this code should be reworked when 18xx
* support is introduced
*/
-#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
+#define SPI_AGGR_BUFFER_SIZE (4 * SZ_4K)
/* Maximum number of SPI write chunks */
#define WSPI_MAX_NUM_OF_CHUNKS \
diff --git a/drivers/net/wireless/wcnss/wcnss_vreg.c b/drivers/net/wireless/wcnss/wcnss_vreg.c
index 82b90ad00f8b..d94bd90f64da 100644
--- a/drivers/net/wireless/wcnss/wcnss_vreg.c
+++ b/drivers/net/wireless/wcnss/wcnss_vreg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2015, 2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -71,23 +71,33 @@ static int is_power_on;
struct vregs_info {
const char * const name;
+ const char * const curr;
+ const char * const volt;
int state;
+ bool required;
struct regulator *regulator;
};
/* IRIS regulators for Pronto hardware */
-static struct vregs_info iris_vregs_pronto[] = {
- {"qcom,iris-vddxo", VREG_NULL_CONFIG, NULL},
- {"qcom,iris-vddrfa", VREG_NULL_CONFIG, NULL},
- {"qcom,iris-vddpa", VREG_NULL_CONFIG, NULL},
- {"qcom,iris-vdddig", VREG_NULL_CONFIG, NULL},
+static struct vregs_info iris_vregs[] = {
+ {"qcom,iris-vddxo", "qcom,iris-vddxo-current",
+ "qcom,iris-vddxo-voltage-level", VREG_NULL_CONFIG, true, NULL},
+ {"qcom,iris-vddrfa", "qcom,iris-vddrfa-current",
+ "qcom,iris-vddrfa-voltage-level", VREG_NULL_CONFIG, true, NULL},
+ {"qcom,iris-vddpa", "qcom,iris-vddpa-current",
+ "qcom,iris-vddpa-voltage-level", VREG_NULL_CONFIG, false, NULL},
+ {"qcom,iris-vdddig", "qcom,iris-vdddig-current",
+ "qcom,iris-vdddig-voltage-level", VREG_NULL_CONFIG, true, NULL},
};
/* WCNSS regulators for Pronto hardware */
static struct vregs_info pronto_vregs[] = {
- {"qcom,pronto-vddmx", VREG_NULL_CONFIG, NULL},
- {"qcom,pronto-vddcx", VREG_NULL_CONFIG, NULL},
- {"qcom,pronto-vddpx", VREG_NULL_CONFIG, NULL},
+ {"qcom,pronto-vddmx", "qcom,pronto-vddmx-current",
+ "qcom,vddmx-voltage-level", VREG_NULL_CONFIG, true, NULL},
+ {"qcom,pronto-vddcx", "qcom,pronto-vddcx-current",
+ "qcom,vddcx-voltage-level", VREG_NULL_CONFIG, true, NULL},
+ {"qcom,pronto-vddpx", "qcom,pronto-vddpx-current",
+ "qcom,vddpx-voltage-level", VREG_NULL_CONFIG, true, NULL},
};
struct host_driver {
@@ -184,6 +194,129 @@ int validate_iris_chip_id(u32 reg)
}
}
+static void wcnss_free_regulator(void)
+{
+ int vreg_i;
+
+ /* Free pronto voltage regulators from device node */
+ for (vreg_i = 0; vreg_i < PRONTO_REGULATORS; vreg_i++) {
+ if (pronto_vregs[vreg_i].state) {
+ regulator_put(pronto_vregs[vreg_i].regulator);
+ pronto_vregs[vreg_i].state = VREG_NULL_CONFIG;
+ }
+ }
+
+ /* Free IRIS voltage regulators from device node */
+ for (vreg_i = 0; vreg_i < IRIS_REGULATORS; vreg_i++) {
+ if (iris_vregs[vreg_i].state) {
+ regulator_put(iris_vregs[vreg_i].regulator);
+ iris_vregs[vreg_i].state = VREG_NULL_CONFIG;
+ }
+ }
+}
+
+static int
+wcnss_dt_parse_vreg_level(struct device *dev, int index,
+ const char *current_vreg_name, const char *vreg_name,
+ struct vregs_level *vlevel)
+{
+ int ret = 0;
+ /* array used to store nominal, low and high voltage values */
+ u32 voltage_levels[3], current_vreg;
+
+ ret = of_property_read_u32_array(dev->of_node, vreg_name,
+ voltage_levels,
+ ARRAY_SIZE(voltage_levels));
+ if (ret) {
+ dev_err(dev, "error reading %s property\n", vreg_name);
+ return ret;
+ }
+
+ vlevel[index].nominal_min = voltage_levels[0];
+ vlevel[index].low_power_min = voltage_levels[1];
+ vlevel[index].max_voltage = voltage_levels[2];
+
+ ret = of_property_read_u32(dev->of_node, current_vreg_name,
+ &current_vreg);
+ if (ret) {
+ dev_err(dev, "error reading %s property\n", current_vreg_name);
+ return ret;
+ }
+
+ vlevel[index].uA_load = current_vreg;
+
+ return ret;
+}
+
+int
+wcnss_parse_voltage_regulator(struct wcnss_wlan_config *wlan_config,
+ struct device *dev)
+{
+ int rc, vreg_i;
+
+ /* Parse pronto voltage regulators from device node */
+ for (vreg_i = 0; vreg_i < PRONTO_REGULATORS; vreg_i++) {
+ pronto_vregs[vreg_i].regulator =
+ regulator_get(dev, pronto_vregs[vreg_i].name);
+ if (IS_ERR(pronto_vregs[vreg_i].regulator)) {
+ if (pronto_vregs[vreg_i].required) {
+ rc = PTR_ERR(pronto_vregs[vreg_i].regulator);
+ dev_err(dev, "regulator get of %s failed (%d)\n",
+ pronto_vregs[vreg_i].name, rc);
+ goto wcnss_vreg_get_err;
+ } else {
+ dev_dbg(dev, "Skip optional regulator configuration: %s\n",
+ pronto_vregs[vreg_i].name);
+ continue;
+ }
+ }
+
+ pronto_vregs[vreg_i].state |= VREG_GET_REGULATOR_MASK;
+ rc = wcnss_dt_parse_vreg_level(dev, vreg_i,
+ pronto_vregs[vreg_i].curr,
+ pronto_vregs[vreg_i].volt,
+ wlan_config->pronto_vlevel);
+ if (rc) {
+ dev_err(dev, "error reading voltage-level property\n");
+ goto wcnss_vreg_get_err;
+ }
+ }
+
+ /* Parse iris voltage regulators from device node */
+ for (vreg_i = 0; vreg_i < IRIS_REGULATORS; vreg_i++) {
+ iris_vregs[vreg_i].regulator =
+ regulator_get(dev, iris_vregs[vreg_i].name);
+ if (IS_ERR(iris_vregs[vreg_i].regulator)) {
+ if (iris_vregs[vreg_i].required) {
+ rc = PTR_ERR(iris_vregs[vreg_i].regulator);
+ dev_err(dev, "regulator get of %s failed (%d)\n",
+ iris_vregs[vreg_i].name, rc);
+ goto wcnss_vreg_get_err;
+ } else {
+ dev_dbg(dev, "Skip optional regulator configuration: %s\n",
+ iris_vregs[vreg_i].name);
+ continue;
+ }
+ }
+
+ iris_vregs[vreg_i].state |= VREG_GET_REGULATOR_MASK;
+ rc = wcnss_dt_parse_vreg_level(dev, vreg_i,
+ iris_vregs[vreg_i].curr,
+ iris_vregs[vreg_i].volt,
+ wlan_config->iris_vlevel);
+ if (rc) {
+ dev_err(dev, "error reading voltage-level property\n");
+ goto wcnss_vreg_get_err;
+ }
+ }
+
+ return 0;
+
+wcnss_vreg_get_err:
+ wcnss_free_regulator();
+ return rc;
+}
+
void wcnss_iris_reset(u32 reg, void __iomem *pmu_conf_reg)
{
/* Reset IRIS */
@@ -419,11 +552,11 @@ static void wcnss_vregs_off(struct vregs_info regulators[], uint size,
/* Remove PWM mode */
if (regulators[i].state & VREG_OPTIMUM_MODE_MASK) {
- rc = regulator_set_optimum_mode(
- regulators[i].regulator, 0);
- if (rc < 0)
- pr_err("regulator_set_optimum_mode(%s) failed (%d)\n",
- regulators[i].name, rc);
+ rc = regulator_set_load(regulators[i].regulator, 0);
+ if (rc < 0) {
+ pr_err("regulator set load(%s) failed (%d)\n",
+ regulators[i].name, rc);
+ }
}
/* Set voltage to lowest level */
@@ -478,18 +611,10 @@ static int wcnss_vregs_on(struct device *dev,
}
for (i = 0; i < size; i++) {
- /* Get regulator source */
- regulators[i].regulator =
- regulator_get(dev, regulators[i].name);
- if (IS_ERR(regulators[i].regulator)) {
- rc = PTR_ERR(regulators[i].regulator);
- pr_err("regulator get of %s failed (%d)\n",
- regulators[i].name, rc);
- goto fail;
- }
- regulators[i].state |= VREG_GET_REGULATOR_MASK;
- reg_cnt = regulator_count_voltages(regulators[i].regulator);
+ if (regulators[i].state == VREG_NULL_CONFIG)
+ continue;
+ reg_cnt = regulator_count_voltages(regulators[i].regulator);
/* Set voltage to nominal. Exclude swtiches e.g. LVS */
if ((voltage_level[i].nominal_min ||
voltage_level[i].max_voltage) && (reg_cnt > 0)) {
@@ -518,11 +643,11 @@ static int wcnss_vregs_on(struct device *dev,
/* Vote for PWM/PFM mode if needed */
if (voltage_level[i].uA_load && (reg_cnt > 0)) {
- rc = regulator_set_optimum_mode(regulators[i].regulator,
- voltage_level[i].uA_load);
+ rc = regulator_set_load(regulators[i].regulator,
+ voltage_level[i].uA_load);
if (rc < 0) {
- pr_err("regulator_set_optimum_mode(%s) failed (%d)\n",
- regulators[i].name, rc);
+ pr_err("regulator set load(%s) failed (%d)\n",
+ regulators[i].name, rc);
goto fail;
}
regulators[i].state |= VREG_OPTIMUM_MODE_MASK;
@@ -551,8 +676,7 @@ static void wcnss_iris_vregs_off(enum wcnss_hw_type hw_type,
{
switch (hw_type) {
case WCNSS_PRONTO_HW:
- wcnss_vregs_off(iris_vregs_pronto,
- ARRAY_SIZE(iris_vregs_pronto),
+ wcnss_vregs_off(iris_vregs, ARRAY_SIZE(iris_vregs),
cfg->iris_vlevel);
break;
default:
@@ -569,8 +693,7 @@ static int wcnss_iris_vregs_on(struct device *dev,
switch (hw_type) {
case WCNSS_PRONTO_HW:
- ret = wcnss_vregs_on(dev, iris_vregs_pronto,
- ARRAY_SIZE(iris_vregs_pronto),
+ ret = wcnss_vregs_on(dev, iris_vregs, ARRAY_SIZE(iris_vregs),
cfg->iris_vlevel);
break;
default:
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 505a9e016777..b604f61e1a05 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -36,6 +36,8 @@
#include <linux/qpnp/qpnp-adc.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_qos.h>
+#include <linux/bitops.h>
+#include <soc/qcom/socinfo.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
@@ -56,11 +58,27 @@
#define WCNSS_PM_QOS_TIMEOUT 15000
#define IS_CAL_DATA_PRESENT 0
#define WAIT_FOR_CBC_IND 2
+#define WCNSS_DUAL_BAND_CAPABILITY_OFFSET BIT(8)
/* module params */
#define WCNSS_CONFIG_UNSPECIFIED (-1)
#define UINT32_MAX (0xFFFFFFFFU)
+#define SUBSYS_NOTIF_MIN_INDEX 0
+#define SUBSYS_NOTIF_MAX_INDEX 9
+char *wcnss_subsys_notif_type[] = {
+ "SUBSYS_BEFORE_SHUTDOWN",
+ "SUBSYS_AFTER_SHUTDOWN",
+ "SUBSYS_BEFORE_POWERUP",
+ "SUBSYS_AFTER_POWERUP",
+ "SUBSYS_RAMDUMP_NOTIFICATION",
+ "SUBSYS_POWERUP_FAILURE",
+ "SUBSYS_PROXY_VOTE",
+ "SUBSYS_PROXY_UNVOTE",
+ "SUBSYS_SOC_RESET",
+ "SUBSYS_NOTIF_TYPE_COUNT"
+};
+
static int has_48mhz_xo = WCNSS_CONFIG_UNSPECIFIED;
module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
@@ -119,6 +137,8 @@ static DEFINE_SPINLOCK(reg_spinlock);
#define PRONTO_PMU_COM_CSR_OFFSET 0x1040
#define PRONTO_PMU_SOFT_RESET_OFFSET 0x104C
+#define PRONTO_QFUSE_DUAL_BAND_OFFSET 0x0018
+
#define A2XB_CFG_OFFSET 0x00
#define A2XB_INT_SRC_OFFSET 0x0c
#define A2XB_TSTBUS_CTRL_OFFSET 0x14
@@ -183,11 +203,9 @@ static DEFINE_SPINLOCK(reg_spinlock);
#define WCNSS_MAX_BUILD_VER_LEN 256
#define WCNSS_MAX_CMD_LEN (128)
#define WCNSS_MIN_CMD_LEN (3)
-#define WCNSS_MIN_SERIAL_LEN (6)
/* control messages from userspace */
#define WCNSS_USR_CTRL_MSG_START 0x00000000
-#define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1)
#define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2)
#define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3)
@@ -381,6 +399,7 @@ static struct {
void __iomem *pronto_saw2_base;
void __iomem *pronto_pll_base;
void __iomem *pronto_mcu_base;
+ void __iomem *pronto_qfuse;
void __iomem *wlan_tx_status;
void __iomem *wlan_tx_phy_aborts;
void __iomem *wlan_brdg_err_source;
@@ -397,7 +416,7 @@ static struct {
int user_cal_read;
int user_cal_available;
u32 user_cal_rcvd;
- int user_cal_exp_size;
+ u32 user_cal_exp_size;
int iris_xo_mode_set;
int fw_vbatt_state;
char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE];
@@ -423,6 +442,9 @@ static struct {
int pc_disabled;
struct delayed_work wcnss_pm_qos_del_req;
struct mutex pm_qos_mutex;
+ struct clk *snoc_wcnss;
+ unsigned int snoc_wcnss_clock_freq;
+ bool is_dual_band_disabled;
} *penv = NULL;
static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
@@ -474,34 +496,6 @@ static ssize_t wcnss_wlan_macaddr_show(struct device *dev,
static DEVICE_ATTR(wcnss_mac_addr, S_IRUSR | S_IWUSR,
wcnss_wlan_macaddr_show, wcnss_wlan_macaddr_store);
-static ssize_t wcnss_serial_number_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- if (!penv)
- return -ENODEV;
-
- return scnprintf(buf, PAGE_SIZE, "%08X\n", penv->serial_number);
-}
-
-static ssize_t wcnss_serial_number_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int value;
-
- if (!penv)
- return -ENODEV;
-
- if (sscanf(buf, "%08X", &value) != 1)
- return -EINVAL;
-
- penv->serial_number = value;
- return count;
-}
-
-static DEVICE_ATTR(serial_number, S_IRUSR | S_IWUSR,
- wcnss_serial_number_show, wcnss_serial_number_store);
-
-
static ssize_t wcnss_thermal_mitigation_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -595,7 +589,31 @@ void wcnss_pronto_is_a2xb_bus_stall(void *tst_addr, u32 fifo_mask, char *type)
}
}
-/* Log pronto debug registers before sending reset interrupt */
+int wcnss_get_dual_band_capability_info(struct platform_device *pdev)
+{
+ u32 reg = 0;
+ struct resource *res;
+
+ res = platform_get_resource_byname(
+ pdev, IORESOURCE_MEM, "pronto_qfuse");
+ if (!res)
+ return -EINVAL;
+
+ penv->pronto_qfuse = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(penv->pronto_qfuse))
+ return -ENOMEM;
+
+ reg = readl_relaxed(penv->pronto_qfuse +
+ PRONTO_QFUSE_DUAL_BAND_OFFSET);
+ if (reg & WCNSS_DUAL_BAND_CAPABILITY_OFFSET)
+ penv->is_dual_band_disabled = true;
+ else
+ penv->is_dual_band_disabled = false;
+
+ return 0;
+}
+
+/* Log pronto debug registers during SSR Timeout CB */
void wcnss_pronto_log_debug_regs(void)
{
void __iomem *reg_addr, *tst_addr, *tst_ctrl_addr;
@@ -1146,13 +1164,9 @@ static int wcnss_create_sysfs(struct device *dev)
if (!dev)
return -ENODEV;
- ret = device_create_file(dev, &dev_attr_serial_number);
- if (ret)
- return ret;
-
ret = device_create_file(dev, &dev_attr_thermal_mitigation);
if (ret)
- goto remove_serial;
+ return ret;
ret = device_create_file(dev, &dev_attr_wcnss_version);
if (ret)
@@ -1168,8 +1182,6 @@ remove_version:
device_remove_file(dev, &dev_attr_wcnss_version);
remove_thermal:
device_remove_file(dev, &dev_attr_thermal_mitigation);
-remove_serial:
- device_remove_file(dev, &dev_attr_serial_number);
return ret;
}
@@ -1177,7 +1189,6 @@ remove_serial:
static void wcnss_remove_sysfs(struct device *dev)
{
if (dev) {
- device_remove_file(dev, &dev_attr_serial_number);
device_remove_file(dev, &dev_attr_thermal_mitigation);
device_remove_file(dev, &dev_attr_wcnss_version);
device_remove_file(dev, &dev_attr_wcnss_mac_addr);
@@ -1625,8 +1636,13 @@ EXPORT_SYMBOL(wcnss_unregister_thermal_mitigation);
unsigned int wcnss_get_serial_number(void)
{
- if (penv)
+ if (penv) {
+ penv->serial_number = socinfo_get_serial_number();
+ pr_info("%s: Device serial number: %u\n",
+ __func__, penv->serial_number);
return penv->serial_number;
+ }
+
return 0;
}
EXPORT_SYMBOL(wcnss_get_serial_number);
@@ -1683,6 +1699,14 @@ int wcnss_wlan_iris_xo_mode(void)
}
EXPORT_SYMBOL(wcnss_wlan_iris_xo_mode);
+int wcnss_wlan_dual_band_disabled(void)
+{
+ if (penv && penv->pdev)
+ return penv->is_dual_band_disabled;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(wcnss_wlan_dual_band_disabled);
void wcnss_suspend_notify(void)
{
@@ -2608,15 +2632,6 @@ void process_usr_ctrl_cmd(u8 *buf, size_t len)
switch (cmd) {
- case WCNSS_USR_SERIAL_NUM:
- if (WCNSS_MIN_SERIAL_LEN > len) {
- pr_err("%s: Invalid serial number\n", __func__);
- return;
- }
- penv->serial_number = buf[2] << 24 | buf[3] << 16
- | buf[4] << 8 | buf[5];
- break;
-
case WCNSS_USR_HAS_CAL_DATA:
if (1 < buf[2])
pr_err("%s: Invalid data for cal %d\n", __func__,
@@ -2674,145 +2689,34 @@ static struct miscdevice wcnss_usr_ctrl = {
};
static int
-wcnss_dt_parse_vreg_level(struct device *dev, int index,
- const char *current_vreg_name, const char *vreg_name,
- struct vregs_level *vlevel)
-{
- int ret = 0;
- /* array used to store nominal, low and high voltage values
- */
- u32 voltage_levels[3], current_vreg;
-
- ret = of_property_read_u32_array(dev->of_node, vreg_name,
- voltage_levels,
- ARRAY_SIZE(voltage_levels));
- if (ret) {
- dev_err(dev, "error reading %s property\n", vreg_name);
- return ret;
- }
-
- vlevel[index].nominal_min = voltage_levels[0];
- vlevel[index].low_power_min = voltage_levels[1];
- vlevel[index].max_voltage = voltage_levels[2];
-
- ret = of_property_read_u32(dev->of_node, current_vreg_name,
- &current_vreg);
- if (ret) {
- dev_err(dev, "error reading %s property\n", current_vreg_name);
- return ret;
- }
-
- vlevel[index].uA_load = current_vreg;
-
- return ret;
-}
-
-static int
wcnss_trigger_config(struct platform_device *pdev)
{
int ret;
- int rc, index = 0;
+ int rc;
struct qcom_wcnss_opts *pdata;
struct resource *res;
int is_pronto_vadc;
int is_pronto_v3;
int pil_retry = 0;
- int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
- "qcom,has-pronto-hw");
+ struct device_node *node = (&pdev->dev)->of_node;
+ int has_pronto_hw = of_property_read_bool(node, "qcom,has-pronto-hw");
- is_pronto_vadc = of_property_read_bool(pdev->dev.of_node,
- "qcom,is-pronto-vadc");
+ is_pronto_vadc = of_property_read_bool(node, "qcom,is-pronto-vadc");
+ is_pronto_v3 = of_property_read_bool(node, "qcom,is-pronto-v3");
- is_pronto_v3 = of_property_read_bool(pdev->dev.of_node,
- "qcom,is-pronto-v3");
+ penv->is_vsys_adc_channel =
+ of_property_read_bool(node, "qcom,has-vsys-adc-channel");
+ penv->is_a2xb_split_reg =
+ of_property_read_bool(node, "qcom,has-a2xb-split-reg");
- penv->is_vsys_adc_channel = of_property_read_bool(pdev->dev.of_node,
- "qcom,has-vsys-adc-channel");
-
- penv->is_a2xb_split_reg = of_property_read_bool(pdev->dev.of_node,
- "qcom,has-a2xb-split-reg");
-
- if (of_property_read_u32(pdev->dev.of_node,
- "qcom,wlan-rx-buff-count", &penv->wlan_rx_buff_count)) {
+ if (of_property_read_u32(node, "qcom,wlan-rx-buff-count",
+ &penv->wlan_rx_buff_count)) {
penv->wlan_rx_buff_count = WCNSS_DEF_WLAN_RX_BUFF_COUNT;
}
- ret = wcnss_dt_parse_vreg_level(&pdev->dev, index,
- "qcom,pronto-vddmx-current",
- "qcom,vddmx-voltage-level",
- penv->wlan_config.pronto_vlevel);
-
- if (ret) {
- dev_err(&pdev->dev, "error reading voltage-level property\n");
- goto fail;
- }
-
- index++;
- ret = wcnss_dt_parse_vreg_level(&pdev->dev, index,
- "qcom,pronto-vddcx-current",
- "qcom,vddcx-voltage-level",
- penv->wlan_config.pronto_vlevel);
-
- if (ret) {
- dev_err(&pdev->dev, "error reading voltage-level property\n");
- goto fail;
- }
-
- index++;
- ret = wcnss_dt_parse_vreg_level(&pdev->dev, index,
- "qcom,pronto-vddpx-current",
- "qcom,vddpx-voltage-level",
- penv->wlan_config.pronto_vlevel);
-
- if (ret) {
- dev_err(&pdev->dev, "error reading voltage-level property\n");
- goto fail;
- }
-
- /* assign 0 to index now onwards, index variable re used to
- * represent iris regulator index
- */
- index = 0;
- ret = wcnss_dt_parse_vreg_level(&pdev->dev, index,
- "qcom,iris-vddxo-current",
- "qcom,iris-vddxo-voltage-level",
- penv->wlan_config.iris_vlevel);
-
- if (ret) {
- dev_err(&pdev->dev, "error reading voltage-level property\n");
- goto fail;
- }
-
- index++;
- ret = wcnss_dt_parse_vreg_level(&pdev->dev, index,
- "qcom,iris-vddrfa-current",
- "qcom,iris-vddrfa-voltage-level",
- penv->wlan_config.iris_vlevel);
-
- if (ret) {
- dev_err(&pdev->dev, "error reading voltage-level property\n");
- goto fail;
- }
-
- index++;
- ret = wcnss_dt_parse_vreg_level(&pdev->dev, index,
- "qcom,iris-vddpa-current",
- "qcom,iris-vddpa-voltage-level",
- penv->wlan_config.iris_vlevel);
-
- if (ret) {
- dev_err(&pdev->dev, "error reading voltage-level property\n");
- goto fail;
- }
-
- index++;
- ret = wcnss_dt_parse_vreg_level(&pdev->dev, index,
- "qcom,iris-vdddig-current",
- "qcom,iris-vdddig-voltage-level",
- penv->wlan_config.iris_vlevel);
-
- if (ret) {
- dev_err(&pdev->dev, "error reading voltage-level property\n");
+ rc = wcnss_parse_voltage_regulator(&penv->wlan_config, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev, "Failed to parse voltage regulators\n");
goto fail;
}
@@ -2825,8 +2729,8 @@ wcnss_trigger_config(struct platform_device *pdev)
pdata = pdev->dev.platform_data;
if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo) {
if (has_pronto_hw) {
- has_48mhz_xo = of_property_read_bool(pdev->dev.of_node,
- "qcom,has-48mhz-xo");
+ has_48mhz_xo =
+ of_property_read_bool(node, "qcom,has-48mhz-xo");
} else {
has_48mhz_xo = pdata->has_48mhz_xo;
}
@@ -2837,8 +2741,8 @@ wcnss_trigger_config(struct platform_device *pdev)
penv->wlan_config.is_pronto_v3 = is_pronto_v3;
if (WCNSS_CONFIG_UNSPECIFIED == has_autodetect_xo && has_pronto_hw) {
- has_autodetect_xo = of_property_read_bool(pdev->dev.of_node,
- "qcom,has-autodetect-xo");
+ has_autodetect_xo =
+ of_property_read_bool(node, "qcom,has-autodetect-xo");
}
penv->thermal_mitigation = 0;
@@ -3118,6 +3022,16 @@ wcnss_trigger_config(struct platform_device *pdev)
__func__);
goto fail_ioremap2;
}
+
+ if (of_property_read_bool(node,
+ "qcom,is-dual-band-disabled")) {
+ ret = wcnss_get_dual_band_capability_info(pdev);
+ if (ret) {
+ pr_err(
+ "%s: failed to get dual band info\n", __func__);
+ goto fail_ioremap2;
+ }
+ }
}
penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss");
@@ -3129,11 +3043,26 @@ wcnss_trigger_config(struct platform_device *pdev)
penv->fw_vbatt_state = WCNSS_CONFIG_UNSPECIFIED;
}
+ penv->snoc_wcnss = devm_clk_get(&penv->pdev->dev, "snoc_wcnss");
+ if (IS_ERR(penv->snoc_wcnss)) {
+ pr_err("%s: couldn't get snoc_wcnss\n", __func__);
+ penv->snoc_wcnss = NULL;
+ } else {
+ if (of_property_read_u32(pdev->dev.of_node,
+ "qcom,snoc-wcnss-clock-freq",
+ &penv->snoc_wcnss_clock_freq)) {
+ pr_debug("%s: wcnss snoc clock frequency is not defined\n",
+ __func__);
+ devm_clk_put(&penv->pdev->dev, penv->snoc_wcnss);
+ penv->snoc_wcnss = NULL;
+ }
+ }
+
if (penv->wlan_config.is_pronto_vadc) {
penv->vadc_dev = qpnp_get_vadc(&penv->pdev->dev, "wcnss");
if (IS_ERR(penv->vadc_dev)) {
- pr_err("%s: vadc get failed\n", __func__);
+ pr_debug("%s: vadc get failed\n", __func__);
penv->vadc_dev = NULL;
} else {
rc = wcnss_get_battery_volt(&penv->wlan_config.vbatt);
@@ -3191,6 +3120,38 @@ fail:
return ret;
}
+/* Driver requires to directly vote the snoc clocks
+ * To enable and disable snoc clock, it call
+ * wcnss_snoc_vote function
+ */
+void wcnss_snoc_vote(bool clk_chk_en)
+{
+ int rc;
+
+ if (!penv->snoc_wcnss) {
+ pr_err("%s: couldn't get clk snoc_wcnss\n", __func__);
+ return;
+ }
+
+ if (clk_chk_en) {
+ rc = clk_set_rate(penv->snoc_wcnss,
+ penv->snoc_wcnss_clock_freq);
+ if (rc) {
+ pr_err("%s: snoc_wcnss_clk-clk_set_rate failed =%d\n",
+ __func__, rc);
+ return;
+ }
+
+ if (clk_prepare_enable(penv->snoc_wcnss)) {
+ pr_err("%s: snoc_wcnss clk enable failed\n", __func__);
+ return;
+ }
+ } else {
+ clk_disable_unprepare(penv->snoc_wcnss);
+ }
+}
+EXPORT_SYMBOL(wcnss_snoc_vote);
+
/* wlan prop driver cannot invoke cancel_work_sync
* function directly, so to invoke this function it
* call wcnss_flush_work function
@@ -3377,7 +3338,15 @@ static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
struct notif_data *data = (struct notif_data *)ss_handle;
int ret, xo_mode;
- pr_info("%s: wcnss notification event: %lu\n", __func__, code);
+ if (!(code >= SUBSYS_NOTIF_MIN_INDEX) &&
+ (code <= SUBSYS_NOTIF_MAX_INDEX)) {
+ pr_debug("%s: Invaild subsystem notification code: %lu\n",
+ __func__, code);
+ return NOTIFY_DONE;
+ }
+
+ pr_debug("%s: wcnss notification event: %lu : %s\n",
+ __func__, code, wcnss_subsys_notif_type[code]);
if (code == SUBSYS_PROXY_VOTE) {
if (pdev && pwlanconfig) {
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index 0333ab0fd926..34173b5e886f 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -201,6 +201,7 @@ struct xenvif_queue { /* Per-queue data for xenvif */
unsigned long remaining_credit;
struct timer_list credit_timeout;
u64 credit_window_start;
+ bool rate_limited;
/* Statistics */
struct xenvif_stats stats;
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index e7bd63eb2876..60b26f32d31d 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -105,7 +105,11 @@ static int xenvif_poll(struct napi_struct *napi, int budget)
if (work_done < budget) {
napi_complete(napi);
- xenvif_napi_schedule_or_enable_events(queue);
+ /* If the queue is rate-limited, it shall be
+ * rescheduled in the timer callback.
+ */
+ if (likely(!queue->rate_limited))
+ xenvif_napi_schedule_or_enable_events(queue);
}
return work_done;
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 1049c34e7d43..72ee1c305cc4 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -687,6 +687,7 @@ static void tx_add_credit(struct xenvif_queue *queue)
max_credit = ULONG_MAX; /* wrapped: clamp to ULONG_MAX */
queue->remaining_credit = min(max_credit, max_burst);
+ queue->rate_limited = false;
}
void xenvif_tx_credit_callback(unsigned long data)
@@ -1184,8 +1185,10 @@ static bool tx_credit_exceeded(struct xenvif_queue *queue, unsigned size)
msecs_to_jiffies(queue->credit_usec / 1000);
/* Timer could already be pending in rare cases. */
- if (timer_pending(&queue->credit_timeout))
+ if (timer_pending(&queue->credit_timeout)) {
+ queue->rate_limited = true;
return true;
+ }
/* Passed the point where we can replenish credit? */
if (time_after_eq64(now, next_credit)) {
@@ -1200,6 +1203,7 @@ static bool tx_credit_exceeded(struct xenvif_queue *queue, unsigned size)
mod_timer(&queue->credit_timeout,
next_credit);
queue->credit_window_start = next_credit;
+ queue->rate_limited = true;
return true;
}
diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
index 532db28145c7..2d043415f326 100644
--- a/drivers/nfc/fdp/i2c.c
+++ b/drivers/nfc/fdp/i2c.c
@@ -177,6 +177,16 @@ static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb)
/* Packet that contains a length */
if (tmp[0] == 0 && tmp[1] == 0) {
phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3;
+ /*
+ * Ensure next_read_size does not exceed sizeof(tmp)
+ * for reading that many bytes during next iteration
+ */
+ if (phy->next_read_size > FDP_NCI_I2C_MAX_PAYLOAD) {
+ dev_dbg(&client->dev, "%s: corrupted packet\n",
+ __func__);
+ phy->next_read_size = 5;
+ goto flush;
+ }
} else {
phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
@@ -210,14 +220,14 @@ static irqreturn_t fdp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
struct sk_buff *skb;
int r;
- client = phy->i2c_dev;
- dev_dbg(&client->dev, "%s\n", __func__);
-
if (!phy || irq != phy->i2c_dev->irq) {
WARN_ON_ONCE(1);
return IRQ_NONE;
}
+ client = phy->i2c_dev;
+ dev_dbg(&client->dev, "%s\n", __func__);
+
r = fdp_nci_i2c_read(phy, &skb);
if (r == -EREMOTEIO)
diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c
index f8dcdf4b24f6..af62c4c854f3 100644
--- a/drivers/nfc/nfcmrvl/fw_dnld.c
+++ b/drivers/nfc/nfcmrvl/fw_dnld.c
@@ -459,7 +459,7 @@ int nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv)
INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work);
snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq",
- dev_name(priv->dev));
+ dev_name(&priv->ndev->nfc_dev->dev));
priv->fw_dnld.rx_wq = create_singlethread_workqueue(name);
if (!priv->fw_dnld.rx_wq)
return -ENOMEM;
@@ -496,6 +496,7 @@ int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name)
{
struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
struct nfcmrvl_fw_dnld *fw_dnld = &priv->fw_dnld;
+ int res;
if (!priv->support_fw_dnld)
return -ENOTSUPP;
@@ -511,7 +512,9 @@ int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name)
*/
/* Retrieve FW binary */
- if (request_firmware(&fw_dnld->fw, firmware_name, priv->dev) < 0) {
+ res = request_firmware(&fw_dnld->fw, firmware_name,
+ &ndev->nfc_dev->dev);
+ if (res < 0) {
nfc_err(priv->dev, "failed to retrieve FW %s", firmware_name);
return -ENOENT;
}
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
index 51c8240a1672..a446590a71ca 100644
--- a/drivers/nfc/nfcmrvl/main.c
+++ b/drivers/nfc/nfcmrvl/main.c
@@ -124,12 +124,13 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
memcpy(&priv->config, pdata, sizeof(*pdata));
if (priv->config.reset_n_io) {
- rc = devm_gpio_request_one(dev,
- priv->config.reset_n_io,
- GPIOF_OUT_INIT_LOW,
- "nfcmrvl_reset_n");
- if (rc < 0)
+ rc = gpio_request_one(priv->config.reset_n_io,
+ GPIOF_OUT_INIT_LOW,
+ "nfcmrvl_reset_n");
+ if (rc < 0) {
+ priv->config.reset_n_io = 0;
nfc_err(dev, "failed to request reset_n io\n");
+ }
}
if (phy == NFCMRVL_PHY_SPI) {
@@ -154,7 +155,13 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
if (!priv->ndev) {
nfc_err(dev, "nci_allocate_device failed\n");
rc = -ENOMEM;
- goto error;
+ goto error_free_gpio;
+ }
+
+ rc = nfcmrvl_fw_dnld_init(priv);
+ if (rc) {
+ nfc_err(dev, "failed to initialize FW download %d\n", rc);
+ goto error_free_dev;
}
nci_set_drvdata(priv->ndev, priv);
@@ -162,24 +169,22 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
rc = nci_register_device(priv->ndev);
if (rc) {
nfc_err(dev, "nci_register_device failed %d\n", rc);
- goto error_free_dev;
+ goto error_fw_dnld_deinit;
}
/* Ensure that controller is powered off */
nfcmrvl_chip_halt(priv);
- rc = nfcmrvl_fw_dnld_init(priv);
- if (rc) {
- nfc_err(dev, "failed to initialize FW download %d\n", rc);
- goto error_free_dev;
- }
-
nfc_info(dev, "registered with nci successfully\n");
return priv;
+error_fw_dnld_deinit:
+ nfcmrvl_fw_dnld_deinit(priv);
error_free_dev:
nci_free_device(priv->ndev);
-error:
+error_free_gpio:
+ if (priv->config.reset_n_io)
+ gpio_free(priv->config.reset_n_io);
kfree(priv);
return ERR_PTR(rc);
}
@@ -195,7 +200,7 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
nfcmrvl_fw_dnld_deinit(priv);
if (priv->config.reset_n_io)
- devm_gpio_free(priv->dev, priv->config.reset_n_io);
+ gpio_free(priv->config.reset_n_io);
nci_unregister_device(ndev);
nci_free_device(ndev);
diff --git a/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c
index 83a99e38e7bd..6c0c301611c4 100644
--- a/drivers/nfc/nfcmrvl/uart.c
+++ b/drivers/nfc/nfcmrvl/uart.c
@@ -109,6 +109,7 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
struct nfcmrvl_private *priv;
struct nfcmrvl_platform_data *pdata = NULL;
struct nfcmrvl_platform_data config;
+ struct device *dev = nu->tty->dev;
/*
* Platform data cannot be used here since usually it is already used
@@ -116,9 +117,8 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
* and check if DT entries were added.
*/
- if (nu->tty->dev->parent && nu->tty->dev->parent->of_node)
- if (nfcmrvl_uart_parse_dt(nu->tty->dev->parent->of_node,
- &config) == 0)
+ if (dev && dev->parent && dev->parent->of_node)
+ if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config) == 0)
pdata = &config;
if (!pdata) {
@@ -131,7 +131,7 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
}
priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_UART, nu, &uart_ops,
- nu->tty->dev, pdata);
+ dev, pdata);
if (IS_ERR(priv))
return PTR_ERR(priv);
diff --git a/drivers/nfc/st21nfca/dep.c b/drivers/nfc/st21nfca/dep.c
index 798a32bbac5d..206285210ab5 100644
--- a/drivers/nfc/st21nfca/dep.c
+++ b/drivers/nfc/st21nfca/dep.c
@@ -217,7 +217,8 @@ static int st21nfca_tm_recv_atr_req(struct nfc_hci_dev *hdev,
atr_req = (struct st21nfca_atr_req *)skb->data;
- if (atr_req->length < sizeof(struct st21nfca_atr_req)) {
+ if (atr_req->length < sizeof(struct st21nfca_atr_req) ||
+ atr_req->length > skb->len) {
r = -EPROTO;
goto exit;
}
diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c
index c79d99b24c96..2d4b6e910b87 100644
--- a/drivers/nfc/st21nfca/se.c
+++ b/drivers/nfc/st21nfca/se.c
@@ -321,23 +321,33 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
* AID 81 5 to 16
* PARAMETERS 82 0 to 255
*/
- if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
+ if (skb->len < NFC_MIN_AID_LENGTH + 2 ||
skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
return -EPROTO;
+ /*
+ * Buffer should have enough space for at least
+ * two tag fields + two length fields + aid_len (skb->data[1])
+ */
+ if (skb->len < skb->data[1] + 4)
+ return -EPROTO;
+
transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev,
skb->len - 2, GFP_KERNEL);
transaction->aid_len = skb->data[1];
memcpy(transaction->aid, &skb->data[2],
transaction->aid_len);
+ transaction->params_len = skb->data[transaction->aid_len + 3];
- /* Check next byte is PARAMETERS tag (82) */
+ /* Check next byte is PARAMETERS tag (82) and the length field */
if (skb->data[transaction->aid_len + 2] !=
- NFC_EVT_TRANSACTION_PARAMS_TAG)
+ NFC_EVT_TRANSACTION_PARAMS_TAG ||
+ skb->len < transaction->aid_len + transaction->params_len + 4) {
+ devm_kfree(dev, transaction);
return -EPROTO;
+ }
- transaction->params_len = skb->data[transaction->aid_len + 3];
memcpy(transaction->params, skb->data +
transaction->aid_len + 4, transaction->params_len);
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index ecc6fb9ca92f..3bbdf60f8908 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -599,7 +599,7 @@ static int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt,
if (!mw->virt_addr)
return -ENOMEM;
- if (qp_count % mw_count && mw_num + 1 < qp_count / mw_count)
+ if (mw_num < qp_count % mw_count)
num_qps_mw = qp_count / mw_count + 1;
else
num_qps_mw = qp_count / mw_count;
@@ -947,7 +947,7 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt,
qp->event_handler = NULL;
ntb_qp_link_down_reset(qp);
- if (qp_count % mw_count && mw_num + 1 < qp_count / mw_count)
+ if (mw_num < qp_count % mw_count)
num_qps_mw = qp_count / mw_count + 1;
else
num_qps_mw = qp_count / mw_count;
@@ -1065,8 +1065,8 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev)
qp_count = ilog2(qp_bitmap);
if (max_num_clients && max_num_clients < qp_count)
qp_count = max_num_clients;
- else if (mw_count < qp_count)
- qp_count = mw_count;
+ else if (nt->mw_count < qp_count)
+ qp_count = nt->mw_count;
qp_bitmap &= BIT_ULL(qp_count) - 1;
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index efb2c1ceef98..957234272ef7 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -1205,10 +1205,13 @@ static int btt_rw_page(struct block_device *bdev, sector_t sector,
struct page *page, int rw)
{
struct btt *btt = bdev->bd_disk->private_data;
+ int rc;
- btt_do_bvec(btt, NULL, page, PAGE_CACHE_SIZE, 0, rw, sector);
- page_endio(page, rw & WRITE, 0);
- return 0;
+ rc = btt_do_bvec(btt, NULL, page, PAGE_CACHE_SIZE, 0, rw, sector);
+ if (rc == 0)
+ page_endio(page, rw & WRITE, 0);
+
+ return rc;
}
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index b7971d410b60..74e5360c53f0 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -88,7 +88,7 @@ static struct nvmem_config imx_ocotp_nvmem_config = {
static const struct of_device_id imx_ocotp_dt_ids[] = {
{ .compatible = "fsl,imx6q-ocotp", (void *)128 },
- { .compatible = "fsl,imx6sl-ocotp", (void *)32 },
+ { .compatible = "fsl,imx6sl-ocotp", (void *)64 },
{ .compatible = "fsl,imx6sx-ocotp", (void *)128 },
{ },
};
diff --git a/drivers/of/device.c b/drivers/of/device.c
index e5f47cec75f3..97a280d50d6d 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -225,6 +225,7 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len)
return tsize;
}
+EXPORT_SYMBOL_GPL(of_device_get_modalias);
/**
* of_device_uevent - Display OF related uevent information
@@ -287,3 +288,4 @@ int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
+EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c
index 8e11fb2831cd..34f1d6b41fb9 100644
--- a/drivers/parisc/ccio-dma.c
+++ b/drivers/parisc/ccio-dma.c
@@ -741,6 +741,8 @@ ccio_map_single(struct device *dev, void *addr, size_t size,
BUG_ON(!dev);
ioc = GET_IOC(dev);
+ if (!ioc)
+ return DMA_ERROR_CODE;
BUG_ON(size <= 0);
@@ -805,6 +807,10 @@ ccio_unmap_single(struct device *dev, dma_addr_t iova, size_t size,
BUG_ON(!dev);
ioc = GET_IOC(dev);
+ if (!ioc) {
+ WARN_ON(!ioc);
+ return;
+ }
DBG_RUN("%s() iovp 0x%lx/%x\n",
__func__, (long)iova, size);
@@ -908,6 +914,8 @@ ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
BUG_ON(!dev);
ioc = GET_IOC(dev);
+ if (!ioc)
+ return 0;
DBG_RUN_SG("%s() START %d entries\n", __func__, nents);
@@ -980,6 +988,10 @@ ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
BUG_ON(!dev);
ioc = GET_IOC(dev);
+ if (!ioc) {
+ WARN_ON(!ioc);
+ return;
+ }
DBG_RUN_SG("%s() START %d entries, %p,%x\n",
__func__, nents, sg_virt(sglist), sglist->length);
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index a0580afe1713..005ea632ba53 100644
--- a/drivers/parisc/dino.c
+++ b/drivers/parisc/dino.c
@@ -154,7 +154,10 @@ struct dino_device
};
/* Looks nice and keeps the compiler happy */
-#define DINO_DEV(d) ((struct dino_device *) d)
+#define DINO_DEV(d) ({ \
+ void *__pdata = d; \
+ BUG_ON(!__pdata); \
+ (struct dino_device *)__pdata; })
/*
@@ -951,7 +954,7 @@ static int __init dino_probe(struct parisc_device *dev)
dino_dev->hba.dev = dev;
dino_dev->hba.base_addr = ioremap_nocache(hpa, 4096);
- dino_dev->hba.lmmio_space_offset = 0; /* CPU addrs == bus addrs */
+ dino_dev->hba.lmmio_space_offset = PCI_F_EXTEND;
spin_lock_init(&dino_dev->dinosaur_pen);
dino_dev->hba.iommu = ccio_get_iommu(dev);
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
index 42844c2bc065..d0c2759076a2 100644
--- a/drivers/parisc/lba_pci.c
+++ b/drivers/parisc/lba_pci.c
@@ -111,8 +111,10 @@ static u32 lba_t32;
/* Looks nice and keeps the compiler happy */
-#define LBA_DEV(d) ((struct lba_device *) (d))
-
+#define LBA_DEV(d) ({ \
+ void *__pdata = d; \
+ BUG_ON(!__pdata); \
+ (struct lba_device *)__pdata; })
/*
** Only allow 8 subsidiary busses per LBA
diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c
index 225049b492e5..d6326144ce01 100644
--- a/drivers/parisc/sba_iommu.c
+++ b/drivers/parisc/sba_iommu.c
@@ -691,6 +691,8 @@ static int sba_dma_supported( struct device *dev, u64 mask)
return 0;
ioc = GET_IOC(dev);
+ if (!ioc)
+ return 0;
/*
* check if mask is >= than the current max IO Virt Address
@@ -722,6 +724,8 @@ sba_map_single(struct device *dev, void *addr, size_t size,
int pide;
ioc = GET_IOC(dev);
+ if (!ioc)
+ return DMA_ERROR_CODE;
/* save offset bits */
offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK;
@@ -803,6 +807,10 @@ sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size,
DBG_RUN("%s() iovp 0x%lx/%x\n", __func__, (long) iova, size);
ioc = GET_IOC(dev);
+ if (!ioc) {
+ WARN_ON(!ioc);
+ return;
+ }
offset = iova & ~IOVP_MASK;
iova ^= offset; /* clear offset bits */
size += offset;
@@ -942,6 +950,8 @@ sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
DBG_RUN_SG("%s() START %d entries\n", __func__, nents);
ioc = GET_IOC(dev);
+ if (!ioc)
+ return 0;
/* Fast path single entry scatterlists. */
if (nents == 1) {
@@ -1027,6 +1037,10 @@ sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
__func__, nents, sg_virt(sglist), sglist->length);
ioc = GET_IOC(dev);
+ if (!ioc) {
+ WARN_ON(!ioc);
+ return;
+ }
#ifdef SBA_COLLECT_STATS
ioc->usg_calls++;
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 217c7ce3f57b..a741c9c7d115 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -278,6 +278,7 @@
#define PERST_PROPAGATION_DELAY_US_MIN 1000
#define PERST_PROPAGATION_DELAY_US_MAX 1005
+#define SWITCH_DELAY_MAX 20
#define REFCLK_STABILIZATION_DELAY_US_MIN 1000
#define REFCLK_STABILIZATION_DELAY_US_MAX 1005
#define LINK_UP_TIMEOUT_US_MIN 5000
@@ -595,7 +596,6 @@ struct msm_pcie_dev_t {
bool cfg_access;
spinlock_t cfg_lock;
unsigned long irqsave_flags;
- struct mutex enumerate_lock;
struct mutex setup_lock;
struct irq_domain *irq_domain;
@@ -626,6 +626,7 @@ struct msm_pcie_dev_t {
bool ext_ref_clk;
bool common_phy;
uint32_t ep_latency;
+ uint32_t switch_latency;
uint32_t wr_halt_size;
uint32_t cpl_timeout;
uint32_t current_bdf;
@@ -702,6 +703,9 @@ static u32 num_rc_on;
/* global lock for PCIe common PHY */
static struct mutex com_phy_lock;
+/* global lock for PCIe enumeration */
+static struct mutex enumerate_lock;
+
/* Table to track info of PCIe devices */
static struct msm_pcie_device_info
msm_pcie_dev_tbl[MAX_RC_NUM * MAX_DEVICE_NUM];
@@ -1735,7 +1739,8 @@ static bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev)
static int msm_pcie_restore_sec_config(struct msm_pcie_dev_t *dev)
{
- int ret, scm_ret;
+ int ret;
+ u64 scm_ret;
if (!dev) {
pr_err("PCIe: the input pcie dev is NULL.\n");
@@ -1745,7 +1750,7 @@ static int msm_pcie_restore_sec_config(struct msm_pcie_dev_t *dev)
ret = scm_restore_sec_cfg(dev->scm_dev_id, 0, &scm_ret);
if (ret || scm_ret) {
PCIE_ERR(dev,
- "PCIe: RC%d failed(%d) to restore sec config, scm_ret=%d\n",
+ "PCIe: RC%d failed(%d) to restore sec config, scm_ret=%llu\n",
dev->rc_idx, ret, scm_ret);
return ret ? ret : -EINVAL;
}
@@ -1984,6 +1989,8 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
dev->common_phy);
PCIE_DBG_FS(dev, "ep_latency: %dms\n",
dev->ep_latency);
+ PCIE_DBG_FS(dev, "switch_latency: %dms\n",
+ dev->switch_latency);
PCIE_DBG_FS(dev, "wr_halt_size: 0x%x\n",
dev->wr_halt_size);
PCIE_DBG_FS(dev, "cpl_timeout: 0x%x\n",
@@ -4675,7 +4682,15 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options)
goto link_fail;
}
- msleep(500);
+ if (dev->switch_latency) {
+ PCIE_DBG(dev, "switch_latency: %dms\n",
+ dev->switch_latency);
+ if (dev->switch_latency <= SWITCH_DELAY_MAX)
+ usleep_range(dev->switch_latency * 1000,
+ dev->switch_latency * 1000);
+ else
+ msleep(dev->switch_latency);
+ }
msm_pcie_config_controller(dev);
@@ -5037,7 +5052,7 @@ int msm_pcie_enumerate(u32 rc_idx)
int ret = 0, bus_ret = 0, scan_ret = 0;
struct msm_pcie_dev_t *dev = &msm_pcie_dev[rc_idx];
- mutex_lock(&dev->enumerate_lock);
+ mutex_lock(&enumerate_lock);
PCIE_DBG(dev, "Enumerate RC%d\n", rc_idx);
@@ -5156,7 +5171,7 @@ int msm_pcie_enumerate(u32 rc_idx)
}
out:
- mutex_unlock(&dev->enumerate_lock);
+ mutex_unlock(&enumerate_lock);
return ret;
}
@@ -6279,6 +6294,20 @@ static int msm_pcie_probe(struct platform_device *pdev)
PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: ep-latency: 0x%x.\n",
rc_idx, msm_pcie_dev[rc_idx].ep_latency);
+ msm_pcie_dev[rc_idx].switch_latency = 0;
+ ret = of_property_read_u32((&pdev->dev)->of_node,
+ "qcom,switch-latency",
+ &msm_pcie_dev[rc_idx].switch_latency);
+
+ if (ret)
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: switch-latency does not exist.\n",
+ rc_idx);
+ else
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: switch-latency: 0x%x.\n",
+ rc_idx, msm_pcie_dev[rc_idx].switch_latency);
+
msm_pcie_dev[rc_idx].wr_halt_size = 0;
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,wr-halt-size",
@@ -6604,6 +6633,7 @@ int __init pcie_init(void)
pcie_drv.rc_num = 0;
mutex_init(&pcie_drv.drv_lock);
mutex_init(&com_phy_lock);
+ mutex_init(&enumerate_lock);
for (i = 0; i < MAX_RC_NUM; i++) {
snprintf(rc_name, MAX_RC_NAME_LEN, "pcie%d-short", i);
@@ -6638,7 +6668,6 @@ int __init pcie_init(void)
rc_name, i);
spin_lock_init(&msm_pcie_dev[i].cfg_lock);
msm_pcie_dev[i].cfg_access = true;
- mutex_init(&msm_pcie_dev[i].enumerate_lock);
mutex_init(&msm_pcie_dev[i].setup_lock);
mutex_init(&msm_pcie_dev[i].recovery_lock);
spin_lock_init(&msm_pcie_dev[i].linkdown_lock);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index d7ffd66814bb..fca925543fae 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -945,6 +945,7 @@ static int pci_pm_thaw_noirq(struct device *dev)
return pci_legacy_resume_early(dev);
pci_update_current_state(pci_dev, PCI_D0);
+ pci_restore_state(pci_dev);
if (drv && drv->pm && drv->pm->thaw_noirq)
error = drv->pm->thaw_noirq(dev);
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 63ec68e6ac2a..39400dda27c2 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -552,6 +552,7 @@ static void armpmu_init(struct arm_pmu *armpmu)
.stop = armpmu_stop,
.read = armpmu_read,
.filter_match = armpmu_filter_match,
+ .events_across_hotplug = 1,
};
}
diff --git a/drivers/pinctrl/freescale/pinctrl-mxs.c b/drivers/pinctrl/freescale/pinctrl-mxs.c
index 6bbda6b4ab50..5da9c95dccb7 100644
--- a/drivers/pinctrl/freescale/pinctrl-mxs.c
+++ b/drivers/pinctrl/freescale/pinctrl-mxs.c
@@ -195,6 +195,16 @@ static int mxs_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
return 0;
}
+static void mxs_pinctrl_rmwl(u32 value, u32 mask, u8 shift, void __iomem *reg)
+{
+ u32 tmp;
+
+ tmp = readl(reg);
+ tmp &= ~(mask << shift);
+ tmp |= value << shift;
+ writel(tmp, reg);
+}
+
static int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
unsigned group)
{
@@ -212,8 +222,7 @@ static int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
reg += bank * 0x20 + pin / 16 * 0x10;
shift = pin % 16 * 2;
- writel(0x3 << shift, reg + CLR);
- writel(g->muxsel[i] << shift, reg + SET);
+ mxs_pinctrl_rmwl(g->muxsel[i], 0x3, shift, reg);
}
return 0;
@@ -280,8 +289,7 @@ static int mxs_pinconf_group_set(struct pinctrl_dev *pctldev,
/* mA */
if (config & MA_PRESENT) {
shift = pin % 8 * 4;
- writel(0x3 << shift, reg + CLR);
- writel(ma << shift, reg + SET);
+ mxs_pinctrl_rmwl(ma, 0x3, shift, reg);
}
/* vol */
diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c
index 9677807db364..b505b87661f8 100644
--- a/drivers/pinctrl/meson/pinctrl-meson8b.c
+++ b/drivers/pinctrl/meson/pinctrl-meson8b.c
@@ -732,8 +732,8 @@ static const char * const sdxc_c_groups[] = {
static const char * const nand_groups[] = {
"nand_io", "nand_io_ce0", "nand_io_ce1",
"nand_io_rb0", "nand_ale", "nand_cle",
- "nand_wen_clk", "nand_ren_clk", "nand_dqs0",
- "nand_dqs1"
+ "nand_wen_clk", "nand_ren_clk", "nand_dqs_0",
+ "nand_dqs_1"
};
static const char * const nor_groups[] = {
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index 71ccf6a90b22..2551e4adb33f 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -194,8 +194,6 @@ static int exynos_irq_request_resources(struct irq_data *irqd)
spin_unlock_irqrestore(&bank->slock, flags);
- exynos_irq_unmask(irqd);
-
return 0;
}
@@ -216,8 +214,6 @@ static void exynos_irq_release_resources(struct irq_data *irqd)
shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
- exynos_irq_mask(irqd);
-
spin_lock_irqsave(&bank->slock, flags);
con = readl(d->virt_base + reg_con);
diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c
index 2b0d70217bbd..699efb1a8c45 100644
--- a/drivers/pinctrl/sh-pfc/core.c
+++ b/drivers/pinctrl/sh-pfc/core.c
@@ -543,6 +543,9 @@ static int sh_pfc_probe(struct platform_device *pdev)
ret = info->ops->init(pfc);
if (ret < 0)
return ret;
+
+ /* .init() may have overridden pfc->info */
+ info = pfc->info;
}
/* Enable dummy states for those platforms without pinctrl support */
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c
index 87a4f44147c1..42ffa8708abc 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c
@@ -1102,7 +1102,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP6_5_3, FMIN_E, SEL_FM_4),
PINMUX_IPSR_DATA(IP6_7_6, AUDIO_CLKOUT),
PINMUX_IPSR_MSEL(IP6_7_6, MSIOF1_SS1_B, SEL_SOF1_1),
- PINMUX_IPSR_MSEL(IP6_5_3, TX2, SEL_SCIF2_0),
+ PINMUX_IPSR_MSEL(IP6_7_6, TX2, SEL_SCIF2_0),
PINMUX_IPSR_MSEL(IP6_7_6, SCIFA2_TXD, SEL_SCIFA2_0),
PINMUX_IPSR_DATA(IP6_9_8, IRQ0),
PINMUX_IPSR_MSEL(IP6_9_8, SCIFB1_RXD_D, SEL_SCIFB1_3),
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c
index 862a096c5dba..be5c71df148d 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c
@@ -811,6 +811,7 @@ static const struct sunxi_desc_pin sun4i_a10_pins[] = {
SUNXI_FUNCTION(0x2, "lcd1"), /* D16 */
SUNXI_FUNCTION(0x3, "pata"), /* ATAD12 */
SUNXI_FUNCTION(0x4, "keypad"), /* IN6 */
+ SUNXI_FUNCTION(0x5, "sim"), /* DET */
SUNXI_FUNCTION_IRQ(0x6, 16), /* EINT16 */
SUNXI_FUNCTION(0x7, "csi1")), /* D16 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 17),
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c
index 90b973e15982..a7c81e988656 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c
@@ -394,7 +394,7 @@ static const struct sunxi_desc_pin sun8i_a83t_pins[] = {
SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 18),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
- SUNXI_FUNCTION(0x3, "owa")), /* DOUT */
+ SUNXI_FUNCTION(0x3, "spdif")), /* DOUT */
SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 19),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out")),
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index b111a5904952..2ae2438032b7 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -1901,6 +1901,20 @@ int gsi_stop_channel(unsigned long chan_hdl)
res = wait_for_completion_timeout(&ctx->compl,
msecs_to_jiffies(GSI_STOP_CMD_TIMEOUT_MS));
if (res == 0) {
+ /*
+ * check channel state here in case the channel is stopped but
+ * the interrupt was not handled yet.
+ */
+ val = gsi_readl(gsi_ctx->base +
+ GSI_EE_n_GSI_CH_k_CNTXT_0_OFFS(chan_hdl,
+ gsi_ctx->per.ee));
+ ctx->state = (val &
+ GSI_EE_n_GSI_CH_k_CNTXT_0_CHSTATE_BMSK) >>
+ GSI_EE_n_GSI_CH_k_CNTXT_0_CHSTATE_SHFT;
+ if (ctx->state == GSI_CHAN_STATE_STOPPED) {
+ res = GSI_STATUS_SUCCESS;
+ goto free_lock;
+ }
GSIDBG("chan_hdl=%lu timed out\n", chan_hdl);
res = -GSI_STATUS_TIMED_OUT;
goto free_lock;
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index 293371b88ab9..8142a5923855 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -902,7 +902,7 @@ int ipa_usb_init_teth_prot(enum ipa_usb_teth_prot teth_prot,
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE ||
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE ||
((teth_prot == IPA_USB_RNDIS || teth_prot == IPA_USB_ECM) &&
teth_params == NULL) || ipa_usb_notify_cb == NULL ||
user_data == NULL) {
@@ -1105,7 +1105,8 @@ static bool ipa3_usb_check_chan_params(struct ipa_usb_xdci_chan_params *params)
params->xfer_scratch.depcmd_hi_addr);
if (params->client >= IPA_CLIENT_MAX ||
- params->teth_prot > IPA_USB_MAX_TETH_PROT_SIZE ||
+ params->teth_prot < 0 ||
+ params->teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE ||
params->xfer_ring_len % GSI_CHAN_RE_SIZE_16B ||
params->xfer_scratch.const_buffer_size < 1 ||
params->xfer_scratch.const_buffer_size > 31) {
@@ -1369,7 +1370,7 @@ static int ipa3_usb_release_xdci_channel(u32 clnt_hdl,
int result = 0;
IPA_USB_DBG_LOW("entry\n");
- if (ttype > IPA_USB_TRANSPORT_MAX) {
+ if (ttype < 0 || ttype >= IPA_USB_TRANSPORT_MAX) {
IPA_USB_ERR("bad parameter.\n");
return -EINVAL;
}
@@ -1473,7 +1474,8 @@ static bool ipa3_usb_check_connect_params(
(params->teth_prot != IPA_USB_DIAG &&
(params->usb_to_ipa_xferrscidx < 0 ||
params->usb_to_ipa_xferrscidx > 127)) ||
- params->teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ params->teth_prot < 0 ||
+ params->teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("Invalid params\n");
return false;
}
@@ -2177,7 +2179,7 @@ EXPORT_SYMBOL(ipa_usb_xdci_connect);
static int ipa3_usb_check_disconnect_prot(enum ipa_usb_teth_prot teth_prot)
{
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("bad parameter.\n");
return -EFAULT;
}
@@ -2367,7 +2369,7 @@ int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot)
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("bad parameters.\n");
result = -EINVAL;
goto bad_params;
@@ -2553,7 +2555,7 @@ int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("bad parameters.\n");
result = -EINVAL;
goto bad_params;
@@ -2754,7 +2756,7 @@ int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("bad parameters.\n");
result = -EINVAL;
goto bad_params;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 85fa9da50779..df741c1c8e5f 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -531,7 +531,7 @@ static void ipa_wan_msg_free_cb(void *buff, u32 len, u32 type)
kfree(buff);
}
-static int ipa_send_wan_msg(unsigned long usr_param, uint8_t msg_type)
+static int ipa_send_wan_msg(unsigned long usr_param, uint8_t msg_type, bool is_cache)
{
int retval;
struct ipa_wan_msg *wan_msg;
@@ -559,6 +559,25 @@ static int ipa_send_wan_msg(unsigned long usr_param, uint8_t msg_type)
return retval;
}
+ if (is_cache) {
+ mutex_lock(&ipa_ctx->ipa_cne_evt_lock);
+
+ /* cache the cne event */
+ memcpy(&ipa_ctx->ipa_cne_evt_req_cache[
+ ipa_ctx->num_ipa_cne_evt_req].wan_msg,
+ wan_msg,
+ sizeof(struct ipa_wan_msg));
+
+ memcpy(&ipa_ctx->ipa_cne_evt_req_cache[
+ ipa_ctx->num_ipa_cne_evt_req].msg_meta,
+ &msg_meta,
+ sizeof(struct ipa_msg_meta));
+
+ ipa_ctx->num_ipa_cne_evt_req++;
+ ipa_ctx->num_ipa_cne_evt_req %= IPA_MAX_NUM_REQ_CACHE;
+ mutex_unlock(&ipa_ctx->ipa_cne_evt_lock);
+ }
+
return 0;
}
@@ -1328,21 +1347,21 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
break;
case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD:
- retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD);
+ retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD, true);
if (retval) {
IPAERR("ipa_send_wan_msg failed: %d\n", retval);
break;
}
break;
case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL:
- retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL);
+ retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL, true);
if (retval) {
IPAERR("ipa_send_wan_msg failed: %d\n", retval);
break;
}
break;
case IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED:
- retval = ipa_send_wan_msg(arg, WAN_EMBMS_CONNECT);
+ retval = ipa_send_wan_msg(arg, WAN_EMBMS_CONNECT, false);
if (retval) {
IPAERR("ipa_send_wan_msg failed: %d\n", retval);
break;
@@ -4165,6 +4184,7 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
mutex_init(&ipa_ctx->lock);
mutex_init(&ipa_ctx->nat_mem.lock);
+ mutex_init(&ipa_ctx->ipa_cne_evt_lock);
idr_init(&ipa_ctx->ipa_idr);
spin_lock_init(&ipa_ctx->idr_lock);
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_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index 51806cec1e4d..49aa7f25347d 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -649,8 +649,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx,
return 0;
ipa_insert_failed:
- if (offset)
- list_move(&offset->link,
+ list_move(&offset->link,
&htbl->head_free_offset_list[offset->bin]);
entry->offset_entry = NULL;
list_del(&entry->link);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index bfb1ce56412c..39d82fab325f 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -65,6 +65,8 @@
#define IPA_IPC_LOG_PAGES 50
+#define IPA_MAX_NUM_REQ_CACHE 10
+
#define IPADBG(fmt, args...) \
do { \
pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\
@@ -996,6 +998,11 @@ struct ipacm_client_info {
bool uplink;
};
+struct ipa_cne_evt {
+ struct ipa_wan_msg wan_msg;
+ struct ipa_msg_meta msg_meta;
+};
+
/**
* struct ipa_context - IPA context
* @class: pointer to the struct class
@@ -1197,6 +1204,9 @@ struct ipa_context {
u32 ipa_rx_max_timeout_usec;
u32 ipa_polling_iteration;
bool ipa_uc_monitor_holb;
+ struct ipa_cne_evt ipa_cne_evt_req_cache[IPA_MAX_NUM_REQ_CACHE];
+ int num_ipa_cne_evt_req;
+ struct mutex ipa_cne_evt_lock;
};
/**
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c
index dd591407d10f..5228b2db1410 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1436,6 +1436,66 @@ struct elem_info ipa_fltr_installed_notif_req_msg_data_v01_ei[] = {
start_ipv6_filter_idx),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(
+ struct ipa_fltr_installed_notif_req_msg_v01,
+ rule_id_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(
+ struct ipa_fltr_installed_notif_req_msg_v01,
+ rule_id_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = QMI_IPA_MAX_FILTERS_V01,
+ .elem_size = sizeof(uint32_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(
+ struct ipa_fltr_installed_notif_req_msg_v01,
+ rule_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct ipa_fltr_installed_notif_req_msg_v01,
+ dst_pipe_id_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct ipa_fltr_installed_notif_req_msg_v01,
+ dst_pipe_id_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = QMI_IPA_MAX_CLIENT_DST_PIPES_V01,
+ .elem_size = sizeof(uint32_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct ipa_fltr_installed_notif_req_msg_v01,
+ dst_pipe_id),
+ },
+ {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 011ca300cc09..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;
}
@@ -1399,7 +1406,7 @@ int ipa2_put_rt_tbl(u32 rt_tbl_hdl)
{
struct ipa_rt_tbl *entry;
enum ipa_ip_type ip = IPA_IP_MAX;
- int result;
+ int result = 0;
mutex_lock(&ipa_ctx->lock);
entry = ipa_id_find(rt_tbl_hdl);
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 834712a71ac6..5dbd43b44540 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -1878,7 +1878,9 @@ void q6_deinitialize_rm(void)
if (ret < 0)
IPAWANERR("Error deleting resource %d, ret=%d\n",
IPA_RM_RESOURCE_Q6_PROD, ret);
- destroy_workqueue(ipa_rm_q6_workqueue);
+
+ if (ipa_rm_q6_workqueue)
+ destroy_workqueue(ipa_rm_q6_workqueue);
}
static void wake_tx_queue(struct work_struct *work)
@@ -2187,7 +2189,10 @@ timer_init_err:
IPAWANERR("Error deleting resource %d, ret=%d\n",
IPA_RM_RESOURCE_WWAN_0_PROD, ret);
create_rsrc_err:
- q6_deinitialize_rm();
+
+ if (!atomic_read(&is_ssr))
+ q6_deinitialize_rm();
+
q6_init_err:
free_netdev(ipa_netdevs[0]);
ipa_netdevs[0] = NULL;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 393ca58a763d..fd503f48f17c 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
@@ -580,7 +598,7 @@ static void ipa3_wan_msg_free_cb(void *buff, u32 len, u32 type)
kfree(buff);
}
-static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type)
+static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type, bool is_cache)
{
int retval;
struct ipa_wan_msg *wan_msg;
@@ -608,9 +626,112 @@ static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type)
return retval;
}
+ if (is_cache) {
+ mutex_lock(&ipa3_ctx->ipa_cne_evt_lock);
+
+ /* cache the cne event */
+ memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[
+ ipa3_ctx->num_ipa_cne_evt_req].wan_msg,
+ wan_msg,
+ sizeof(struct ipa_wan_msg));
+
+ memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[
+ ipa3_ctx->num_ipa_cne_evt_req].msg_meta,
+ &msg_meta,
+ sizeof(struct ipa_msg_meta));
+
+ ipa3_ctx->num_ipa_cne_evt_req++;
+ ipa3_ctx->num_ipa_cne_evt_req %= IPA_MAX_NUM_REQ_CACHE;
+ mutex_unlock(&ipa3_ctx->ipa_cne_evt_lock);
+ }
+
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 +740,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 +782,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 +862,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))) {
@@ -1467,21 +1622,21 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
break;
case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD:
- retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD);
+ retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD, true);
if (retval) {
IPAERR("ipa3_send_wan_msg failed: %d\n", retval);
break;
}
break;
case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL:
- retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL);
+ retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL, true);
if (retval) {
IPAERR("ipa3_send_wan_msg failed: %d\n", retval);
break;
}
break;
case IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED:
- retval = ipa3_send_wan_msg(arg, WAN_EMBMS_CONNECT);
+ retval = ipa3_send_wan_msg(arg, WAN_EMBMS_CONNECT, false);
if (retval) {
IPAERR("ipa3_send_wan_msg failed: %d\n", retval);
break;
@@ -1582,6 +1737,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;
@@ -2248,7 +2431,6 @@ static int ipa3_q6_set_ex_path_to_apps(void)
struct ipahal_imm_cmd_register_write reg_write;
struct ipahal_imm_cmd_pyld *cmd_pyld;
int retval;
- struct ipahal_reg_valmask valmask;
desc = kcalloc(ipa3_ctx->ipa_num_pipes, sizeof(struct ipa3_desc),
GFP_KERNEL);
@@ -2263,40 +2445,10 @@ static int ipa3_q6_set_ex_path_to_apps(void)
if (ep_idx == -1)
continue;
- if (ipa3_ctx->ep[ep_idx].valid &&
- ipa3_ctx->ep[ep_idx].skip_ep_cfg) {
- BUG_ON(num_descs >= ipa3_ctx->ipa_num_pipes);
-
- reg_write.skip_pipeline_clear = false;
- reg_write.pipeline_clear_options =
- IPAHAL_HPS_CLEAR;
- reg_write.offset =
- ipahal_get_reg_n_ofst(IPA_ENDP_STATUS_n,
- ep_idx);
- ipahal_get_status_ep_valmask(
- ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS),
- &valmask);
- reg_write.value = valmask.val;
- reg_write.value_mask = valmask.mask;
- cmd_pyld = ipahal_construct_imm_cmd(
- IPA_IMM_CMD_REGISTER_WRITE, &reg_write, false);
- if (!cmd_pyld) {
- IPAERR("fail construct register_write cmd\n");
- BUG();
- }
-
- desc[num_descs].opcode = ipahal_imm_cmd_get_opcode(
- IPA_IMM_CMD_REGISTER_WRITE);
- desc[num_descs].type = IPA_IMM_CMD_DESC;
- desc[num_descs].callback = ipa3_destroy_imm;
- desc[num_descs].user1 = cmd_pyld;
- desc[num_descs].pyld = cmd_pyld->data;
- desc[num_descs].len = cmd_pyld->len;
- num_descs++;
- }
-
- /* disable statuses for modem producers */
- if (IPA_CLIENT_IS_Q6_PROD(client_idx)) {
+ /* disable statuses for all modem controlled prod pipes */
+ if (IPA_CLIENT_IS_Q6_PROD(client_idx) ||
+ (ipa3_ctx->ep[ep_idx].valid &&
+ ipa3_ctx->ep[ep_idx].skip_ep_cfg)) {
ipa_assert_on(num_descs >= ipa3_ctx->ipa_num_pipes);
reg_write.skip_pipeline_clear = false;
@@ -3028,6 +3180,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;
@@ -3099,6 +3279,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;
@@ -3108,6 +3291,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;
@@ -3977,6 +4163,8 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
/* Prevent consequent calls from trying to load the FW again. */
if (ipa3_ctx->ipa_initialization_complete)
return 0;
+ /* move proxy vote for modem on ipa3_post_init */
+ IPA_ACTIVE_CLIENTS_INC_SPECIAL("PROXY_CLK_VOTE");
if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
memset(&gsi_props, 0, sizeof(gsi_props));
@@ -4104,12 +4292,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) {
@@ -4125,7 +4313,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;
}
@@ -4140,15 +4328,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)) {
@@ -4156,7 +4344,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;
}
@@ -4186,25 +4374,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;
}
@@ -4296,7 +4486,6 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
int i;
struct ipa3_flt_tbl *flt_tbl;
struct ipa3_rt_tbl_set *rset;
- struct ipa_active_client_logging_info log_info;
IPADBG("IPA Driver initialization started\n");
@@ -4486,8 +4675,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
mutex_init(&ipa3_ctx->ipa3_active_clients.mutex);
spin_lock_init(&ipa3_ctx->ipa3_active_clients.spinlock);
- IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, "PROXY_CLK_VOTE");
- ipa3_active_clients_log_inc(&log_info, false);
+ /* move proxy vote for modem to ipa3_post_init() */
ipa3_ctx->ipa3_active_clients.cnt = 1;
/* Assign resource limitation to each group */
@@ -4650,6 +4838,8 @@ 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);
+ mutex_init(&ipa3_ctx->ipa_cne_evt_lock);
idr_init(&ipa3_ctx->ipa_idr);
spin_lock_init(&ipa3_ctx->idr_lock);
@@ -4723,7 +4913,6 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
init_completion(&ipa3_ctx->init_completion_obj);
init_completion(&ipa3_ctx->uc_loaded_completion_obj);
-
/*
* For GSI, we can't register the GSI driver yet, as it expects
* the GSI FW to be up and running before the registration.
@@ -4766,6 +4955,8 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
IPADBG("ipa cdev added successful. major:%d minor:%d\n",
MAJOR(ipa3_ctx->dev_num),
MINOR(ipa3_ctx->dev_num));
+ /* proxy vote for motem is added in ipa3_post_init() phase */
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return 0;
fail_cdev_add:
@@ -5472,6 +5663,10 @@ static int ipa3_smp2p_probe(struct device *dev)
struct device_node *node = dev->of_node;
int res;
+ if (ipa3_ctx == NULL) {
+ IPAERR("ipa3_ctx was not initialized\n");
+ return -ENXIO;
+ }
IPADBG("node->name=%s\n", node->name);
if (strcmp("qcom,smp2pgpio_map_ipa_1_out", node->name) == 0) {
res = of_get_gpio(node, 0);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_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_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index 7c3b5838242e..ce35ba02154d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -426,8 +426,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx,
return 0;
ipa_insert_failed:
- if (offset)
- list_move(&offset->link,
+ list_move(&offset->link,
&htbl->head_free_offset_list[offset->bin]);
entry->offset_entry = NULL;
list_del(&entry->link);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 4278dc45aad2..8ae714d4e7cc 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
@@ -65,6 +64,8 @@
#define IPA_IPC_LOG_PAGES 50
+#define IPA_MAX_NUM_REQ_CACHE 10
+
#define IPADBG(fmt, args...) \
do { \
pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\
@@ -1050,6 +1051,11 @@ struct ipa_dma_task_info {
struct ipahal_imm_cmd_pyld *cmd_pyld;
};
+struct ipa_cne_evt {
+ struct ipa_wan_msg wan_msg;
+ struct ipa_msg_meta msg_meta;
+};
+
/**
* struct ipa3_context - IPA context
* @class: pointer to the struct class
@@ -1231,6 +1237,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;
@@ -1270,6 +1277,9 @@ struct ipa3_context {
u32 ipa_tz_unlock_reg_num;
struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
struct ipa_dma_task_info dma_task_info;
+ struct ipa_cne_evt ipa_cne_evt_req_cache[IPA_MAX_NUM_REQ_CACHE];
+ int num_ipa_cne_evt_req;
+ struct mutex ipa_cne_evt_lock;
};
/**
@@ -1640,12 +1650,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_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
index 690a9db67ff0..571852c076ea 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -807,6 +807,11 @@ int ipa3_qmi_filter_notify_send(
return -EINVAL;
}
+ if (req->source_pipe_index == -1) {
+ IPAWANERR("Source pipe index invalid\n");
+ return -EINVAL;
+ }
+
mutex_lock(&ipa3_qmi_lock);
if (ipa3_qmi_ctx != NULL) {
/* cache the qmi_filter_request */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
index d5d850309696..e6f1e2ce0b75 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
@@ -121,6 +121,31 @@ extern struct elem_info ipa3_init_modem_driver_cmplt_resp_msg_data_v01_ei[];
extern struct elem_info ipa3_install_fltr_rule_req_ex_msg_data_v01_ei[];
extern struct elem_info ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[];
+ extern struct elem_info
+ ipa3_install_fltr_rule_req_ex_msg_data_v01_ei[];
+ extern struct elem_info
+ ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[];
+ extern struct elem_info
+ ipa3_ul_firewall_rule_type_data_v01_ei[];
+ extern struct elem_info
+ ipa3_ul_firewall_config_result_type_data_v01_ei[];
+ extern struct elem_info
+ ipa3_per_client_stats_info_type_data_v01_ei[];
+ extern struct elem_info
+ ipa3_enable_per_client_stats_req_msg_data_v01_ei[];
+ extern struct elem_info
+ ipa3_enable_per_client_stats_resp_msg_data_v01_ei[];
+ extern struct elem_info
+ ipa3_get_stats_per_client_req_msg_data_v01_ei[];
+ extern struct elem_info
+ ipa3_get_stats_per_client_resp_msg_data_v01_ei[];
+ extern struct elem_info
+ ipa3_configure_ul_firewall_rules_req_msg_data_v01_ei[];
+ extern struct elem_info
+ ipa3_configure_ul_firewall_rules_resp_msg_data_v01_ei[];
+ extern struct elem_info
+ ipa3_configure_ul_firewall_rules_ind_msg_data_v01_ei[];
+
/**
* struct ipa3_rmnet_context - IPA rmnet context
* @ipa_rmnet_ssr: support modem SSR
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
index 6a5cb4891c02..746863732dc5 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,8 @@
#include <soc/qcom/msm_qmi_interface.h>
+#include "ipa_qmi_service.h"
+
/* Type Definitions */
static struct elem_info ipa3_hdr_tbl_info_type_data_v01_ei[] = {
{
@@ -1756,6 +1758,36 @@ struct elem_info ipa3_fltr_installed_notif_req_msg_data_v01_ei[] = {
rule_id),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct ipa_fltr_installed_notif_req_msg_v01,
+ dst_pipe_id_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct ipa_fltr_installed_notif_req_msg_v01,
+ dst_pipe_id_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = QMI_IPA_MAX_CLIENT_DST_PIPES_V01,
+ .elem_size = sizeof(uint32_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct ipa_fltr_installed_notif_req_msg_v01,
+ dst_pipe_id),
+ },
+ {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
@@ -2923,3 +2955,435 @@ struct elem_info ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[] = {
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
+
+struct elem_info ipa3_per_client_stats_info_type_data_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ client_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ src_pipe_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ num_ul_ipv4_bytes),
+
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ num_ul_ipv6_bytes),
+
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ num_dl_ipv4_bytes),
+
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ num_dl_ipv6_bytes),
+
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ num_ul_ipv4_pkts),
+
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ num_ul_ipv6_pkts),
+
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ num_dl_ipv4_pkts),
+
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_per_client_stats_info_type_v01,
+ num_dl_ipv6_pkts),
+
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info ipa3_ul_firewall_rule_type_data_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_ul_firewall_rule_type_v01,
+ ip_type),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct ipa_filter_rule_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(struct ipa_ul_firewall_rule_type_v01,
+ filter_rule),
+ .ei_array = ipa3_filter_rule_type_data_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info ipa3_ul_firewall_config_result_type_data_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_ul_firewall_config_result_type_v01,
+ is_success),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(
+ struct ipa_ul_firewall_config_result_type_v01,
+ mux_id),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info ipa3_enable_per_client_stats_req_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct
+ ipa_enable_per_client_stats_req_msg_v01,
+ enable_per_client_stats),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info ipa3_enable_per_client_stats_resp_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(
+ struct ipa_enable_per_client_stats_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info ipa3_get_stats_per_client_req_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(
+ struct ipa_get_stats_per_client_req_msg_v01,
+ client_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(
+ struct ipa_get_stats_per_client_req_msg_v01,
+ src_pipe_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_get_stats_per_client_req_msg_v01,
+ reset_stats_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_get_stats_per_client_req_msg_v01,
+ reset_stats),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info ipa3_get_stats_per_client_resp_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(
+ struct ipa_get_stats_per_client_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_get_stats_per_client_resp_msg_v01,
+ per_client_stats_list_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_get_stats_per_client_resp_msg_v01,
+ per_client_stats_list_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_IPA_MAX_PER_CLIENTS_V01,
+ .elem_size =
+ sizeof(struct ipa_per_client_stats_info_type_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_get_stats_per_client_resp_msg_v01,
+ per_client_stats_list),
+ .ei_array =
+ ipa3_per_client_stats_info_type_data_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info
+ ipa3_configure_ul_firewall_rules_req_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1,
+ .offset = offsetof(
+ struct ipa_configure_ul_firewall_rules_req_msg_v01,
+ firewall_rules_list_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_IPA_MAX_UL_FIREWALL_RULES_V01,
+ .elem_size = sizeof(struct ipa_ul_firewall_rule_type_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x1,
+ .offset = offsetof(
+ struct ipa_configure_ul_firewall_rules_req_msg_v01,
+ firewall_rules_list),
+ .ei_array =
+ ipa3_ul_firewall_rule_type_data_v01_ei,
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x2,
+ .offset = offsetof(
+ struct ipa_configure_ul_firewall_rules_req_msg_v01,
+ mux_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_configure_ul_firewall_rules_req_msg_v01,
+ disable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_configure_ul_firewall_rules_req_msg_v01,
+ disable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct ipa_configure_ul_firewall_rules_req_msg_v01,
+ are_blacklist_filters_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct ipa_configure_ul_firewall_rules_req_msg_v01,
+ are_blacklist_filters),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info
+ ipa3_configure_ul_firewall_rules_resp_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(
+ struct ipa_configure_ul_firewall_rules_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info
+ ipa3_configure_ul_firewall_rules_ind_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(
+ struct ipa_ul_firewall_config_result_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(
+ struct ipa_configure_ul_firewall_rules_ind_msg_v01,
+ result),
+ .ei_array =
+ ipa3_ul_firewall_config_result_type_data_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index bc7cc7060545..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;
@@ -1479,7 +1486,7 @@ int ipa3_put_rt_tbl(u32 rt_tbl_hdl)
{
struct ipa3_rt_tbl *entry;
enum ipa_ip_type ip = IPA_IP_MAX;
- int result;
+ int result = 0;
mutex_lock(&ipa3_ctx->lock);
entry = ipa3_id_find(rt_tbl_hdl);
@@ -1501,6 +1508,7 @@ int ipa3_put_rt_tbl(u32 rt_tbl_hdl)
ip = IPA_IP_v6;
else {
WARN_ON(1);
+ result = -EINVAL;
goto ret;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index e8bd0cd2ffb5..4979f62b928f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -2861,7 +2861,7 @@ static int ipa3_tag_generate_force_close_desc(struct ipa3_desc desc[],
IPAHAL_FULL_PIPELINE_CLEAR;
reg_write_agg_close.offset =
ipahal_get_reg_ofst(IPA_AGGR_FORCE_CLOSE);
- ipahal_get_aggr_force_close_valmask(1<<i, &valmask);
+ ipahal_get_aggr_force_close_valmask(i, &valmask);
reg_write_agg_close.value = valmask.val;
reg_write_agg_close.value_mask = valmask.mask;
cmd_pyld = ipahal_construct_imm_cmd(IPA_IMM_CMD_REGISTER_WRITE,
@@ -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/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
index 0db9f30181a7..d0aa42c81750 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -1576,6 +1576,11 @@ void ipahal_get_aggr_force_close_valmask(int ep_idx,
IPA_AGGR_FORCE_CLOSE_AGGR_FORCE_CLOSE_PIPE_BITMAP_BMSK_V3_5;
}
+ if (ep_idx > (sizeof(valmask->val) * 8 - 1)) {
+ IPAHAL_ERR("too big ep_idx %d\n", ep_idx);
+ ipa_assert();
+ return;
+ }
IPA_SETFIELD_IN_REG(valmask->val, 1 << ep_idx, shft, bmsk);
valmask->mask = bmsk << shft;
}
@@ -1607,20 +1612,3 @@ void ipahal_get_fltrt_hash_flush_valmask(
valmask->mask = valmask->val;
}
-
-void ipahal_get_status_ep_valmask(int pipe_num,
- struct ipahal_reg_valmask *valmask)
-{
- if (!valmask) {
- IPAHAL_ERR("Input error\n");
- return;
- }
-
- valmask->val =
- (pipe_num & IPA_ENDP_STATUS_n_STATUS_ENDP_BMSK) <<
- IPA_ENDP_STATUS_n_STATUS_ENDP_SHFT;
-
- valmask->mask =
- IPA_ENDP_STATUS_n_STATUS_ENDP_BMSK <<
- IPA_ENDP_STATUS_n_STATUS_ENDP_SHFT;
-}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
index 4db09475d5e2..9f32c071cb68 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
@@ -467,8 +467,6 @@ void ipahal_get_aggr_force_close_valmask(int ep_idx,
void ipahal_get_fltrt_hash_flush_valmask(
struct ipahal_reg_fltrt_hash_flush *flush,
struct ipahal_reg_valmask *valmask);
-void ipahal_get_status_ep_valmask(int pipe_num,
- struct ipahal_reg_valmask *valmask);
#endif /* _IPAHAL_REG_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 039bc7da5153..8fbde6675070 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -702,6 +702,11 @@ static int ipa3_wwan_add_ul_flt_rule_to_ipa(void)
/* send ipa_fltr_installed_notif_req_msg_v01 to Q6*/
req->source_pipe_index =
ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_PROD);
+ if (req->source_pipe_index == IPA_EP_NOT_ALLOCATED) {
+ IPAWANERR("ep mapping failed\n");
+ retval = -EFAULT;
+ }
+
req->install_status = QMI_RESULT_SUCCESS_V01;
req->rule_id_valid = 1;
req->rule_id_len = rmnet_ipa3_ctx->num_q6_rules;
@@ -1947,7 +1952,9 @@ void ipa3_q6_deinitialize_rm(void)
if (ret < 0)
IPAWANERR("Error deleting resource %d, ret=%d\n",
IPA_RM_RESOURCE_Q6_PROD, ret);
- destroy_workqueue(rmnet_ipa3_ctx->rm_q6_wq);
+
+ if (rmnet_ipa3_ctx->rm_q6_wq)
+ destroy_workqueue(rmnet_ipa3_ctx->rm_q6_wq);
}
static void ipa3_wake_tx_queue(struct work_struct *work)
@@ -2287,7 +2294,10 @@ timer_init_err:
IPAWANERR("Error deleting resource %d, ret=%d\n",
IPA_RM_RESOURCE_WWAN_0_PROD, ret);
create_rsrc_err:
- ipa3_q6_deinitialize_rm();
+
+ if (!atomic_read(&rmnet_ipa3_ctx->is_ssr))
+ ipa3_q6_deinitialize_rm();
+
q6_init_err:
free_netdev(dev);
rmnet_ipa3_ctx->wwan_priv = NULL;
diff --git a/drivers/platform/msm/mhi/mhi_iface.c b/drivers/platform/msm/mhi/mhi_iface.c
index a5936ea5a6aa..ce6d1257cfbb 100644
--- a/drivers/platform/msm/mhi/mhi_iface.c
+++ b/drivers/platform/msm/mhi/mhi_iface.c
@@ -509,7 +509,7 @@ static int __exit mhi_plat_remove(struct platform_device *pdev)
static int __init mhi_init(void)
{
- int r;
+ int r = -EAGAIN;
struct mhi_device_driver *mhi_dev_drv;
mhi_dev_drv = kmalloc(sizeof(*mhi_dev_drv), GFP_KERNEL);
diff --git a/drivers/platform/msm/mhi/mhi_init.c b/drivers/platform/msm/mhi/mhi_init.c
index b6edf707798b..a95579241524 100644
--- a/drivers/platform/msm/mhi/mhi_init.c
+++ b/drivers/platform/msm/mhi/mhi_init.c
@@ -141,7 +141,7 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
size_t mhi_mem_index = 0, ring_len;
void *dev_mem_start;
dma_addr_t dma_dev_mem_start;
- int i, r;
+ int i;
mhi_dev_ctxt->dev_space.dev_mem_len =
calculate_mhi_space(mhi_dev_ctxt);
@@ -244,7 +244,7 @@ err_ev_alloc:
mhi_dev_ctxt->dev_space.dev_mem_len,
mhi_dev_ctxt->dev_space.dev_mem_start,
mhi_dev_ctxt->dev_space.dma_dev_mem_start);
- return r;
+ return -EFAULT;
}
static int mhi_init_events(struct mhi_device_ctxt *mhi_dev_ctxt)
diff --git a/drivers/platform/msm/mhi/mhi_mmio_ops.c b/drivers/platform/msm/mhi/mhi_mmio_ops.c
index a991a2e68b34..18d0334ce1ec 100644
--- a/drivers/platform/msm/mhi/mhi_mmio_ops.c
+++ b/drivers/platform/msm/mhi/mhi_mmio_ops.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -109,7 +109,6 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
u64 pcie_dword_val = 0;
u32 pcie_word_val = 0;
u32 i = 0;
- int ret_val;
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"~~~ Initializing MMIO ~~~\n");
@@ -131,7 +130,7 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
if (mhi_dev_ctxt->core.mhi_ver != MHI_VERSION) {
mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Bad MMIO version, 0x%x\n", mhi_dev_ctxt->core.mhi_ver);
- return ret_val;
+ return -ENXIO;
}
/* Enable the channels */
diff --git a/drivers/platform/msm/mhi/mhi_sys.c b/drivers/platform/msm/mhi/mhi_sys.c
index 1d9282627d4e..b1434daf1f60 100644
--- a/drivers/platform/msm/mhi/mhi_sys.c
+++ b/drivers/platform/msm/mhi/mhi_sys.c
@@ -329,7 +329,7 @@ uintptr_t mhi_p2v_addr(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_RING_TYPE type,
u32 chan, uintptr_t phy_ptr)
{
- uintptr_t virtual_ptr;
+ uintptr_t virtual_ptr = 0;
struct mhi_ring_ctxt *cs = &mhi_dev_ctxt->dev_space.ring_ctxt;
switch (type) {
@@ -358,7 +358,7 @@ dma_addr_t mhi_v2p_addr(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_RING_TYPE type,
u32 chan, uintptr_t va_ptr)
{
- dma_addr_t phy_ptr;
+ dma_addr_t phy_ptr = 0;
struct mhi_ring_ctxt *cs = &mhi_dev_ctxt->dev_space.ring_ctxt;
switch (type) {
diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c
index 4563810d7e5b..9c35eeb177d9 100644
--- a/drivers/platform/msm/mhi_uci/mhi_uci.c
+++ b/drivers/platform/msm/mhi_uci/mhi_uci.c
@@ -326,73 +326,6 @@ static int mhi_init_inbound(struct uci_client *client_handle)
return ret_val;
}
-static int mhi_uci_send_packet(struct mhi_client_handle **client_handle,
- void *buf,
- u32 size)
-{
- u32 nr_avail_trbs = 0;
- u32 i = 0;
- void *data_loc = NULL;
- unsigned long memcpy_result = 0;
- int data_left_to_insert = 0;
- size_t data_to_insert_now = 0;
- u32 data_inserted_so_far = 0;
- int ret_val = 0;
- struct uci_client *uci_handle;
- struct uci_buf *uci_buf;
-
- uci_handle = container_of(client_handle, struct uci_client,
- out_attr.mhi_handle);
-
- nr_avail_trbs = atomic_read(&uci_handle->out_attr.avail_pkts);
- data_left_to_insert = size;
-
- for (i = 0; i < nr_avail_trbs; ++i) {
- data_to_insert_now = min_t(size_t, data_left_to_insert,
- uci_handle->out_attr.max_packet_size);
- data_loc = kmalloc(data_to_insert_now + sizeof(*uci_buf),
- GFP_KERNEL);
- if (!data_loc) {
- uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
- "Failed to allocate memory 0x%zx\n",
- data_to_insert_now);
- return -ENOMEM;
- }
- uci_buf = data_loc + data_to_insert_now;
- uci_buf->data = data_loc;
- uci_buf->pkt_id = uci_handle->out_attr.pkt_count++;
- memcpy_result = copy_from_user(uci_buf->data,
- buf + data_inserted_so_far,
- data_to_insert_now);
- if (memcpy_result)
- goto error_xfer;
-
- uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
- "At trb i = %d/%d, size = %lu, id %llu chan %d\n",
- i, nr_avail_trbs, data_to_insert_now, uci_buf->pkt_id,
- uci_handle->out_attr.chan_id);
- ret_val = mhi_queue_xfer(*client_handle, uci_buf->data,
- data_to_insert_now, MHI_EOT);
- if (ret_val) {
- goto error_xfer;
- } else {
- data_left_to_insert -= data_to_insert_now;
- data_inserted_so_far += data_to_insert_now;
- atomic_inc(&uci_handle->out_pkt_pend_ack);
- atomic_dec(&uci_handle->out_attr.avail_pkts);
- list_add_tail(&uci_buf->node,
- &uci_handle->out_attr.buf_head);
- }
- if (!data_left_to_insert)
- break;
- }
- return data_inserted_so_far;
-
-error_xfer:
- kfree(uci_buf->data);
- return data_inserted_so_far;
-}
-
static int mhi_uci_send_status_cmd(struct uci_client *client)
{
void *buf = NULL;
@@ -963,18 +896,11 @@ static ssize_t mhi_uci_client_write(struct file *file,
goto sys_interrupt;
}
- while (bytes_transferrd != count) {
- ret_val = mhi_uci_send_packet(&chan_attr->mhi_handle,
- (void *)buf, count);
- if (ret_val < 0)
- goto sys_interrupt;
+ while (count) {
+ size_t xfer_size;
+ void *data_loc = NULL;
+ struct uci_buf *uci_buf;
- bytes_transferrd += ret_val;
- if (bytes_transferrd == count)
- break;
- uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
- "No descriptors available, did we poll, chan %d?\n",
- chan);
mutex_unlock(&chan_attr->chan_lock);
ret_val = wait_event_interruptible(chan_attr->wq,
(atomic_read(&chan_attr->avail_pkts) ||
@@ -991,6 +917,37 @@ static ssize_t mhi_uci_client_write(struct file *file,
ret_val = -ERESTARTSYS;
goto sys_interrupt;
}
+
+ xfer_size = min_t(size_t, count, chan_attr->max_packet_size);
+ data_loc = kmalloc(xfer_size + sizeof(*uci_buf), GFP_KERNEL);
+ if (!data_loc) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
+ "Failed to allocate memory %lu\n", xfer_size);
+ ret_val = -ENOMEM;
+ goto sys_interrupt;
+ }
+
+ uci_buf = data_loc + xfer_size;
+ uci_buf->data = data_loc;
+ uci_buf->pkt_id = uci_handle->out_attr.pkt_count++;
+ ret_val = copy_from_user(uci_buf->data, buf, xfer_size);
+ if (unlikely(ret_val)) {
+ kfree(uci_buf->data);
+ goto sys_interrupt;
+ }
+ ret_val = mhi_queue_xfer(chan_attr->mhi_handle, uci_buf->data,
+ xfer_size, MHI_EOT);
+ if (unlikely(ret_val)) {
+ kfree(uci_buf->data);
+ goto sys_interrupt;
+ }
+
+ bytes_transferrd += xfer_size;
+ count -= xfer_size;
+ buf += xfer_size;
+ atomic_inc(&uci_handle->out_pkt_pend_ack);
+ atomic_dec(&uci_handle->out_attr.avail_pkts);
+ list_add_tail(&uci_buf->node, &uci_handle->out_attr.buf_head);
}
mutex_unlock(&chan_attr->chan_lock);
@@ -1073,7 +1030,7 @@ error_dts:
static void process_rs232_state(struct uci_client *ctrl_client,
struct mhi_result *result)
{
- struct rs232_ctrl_msg *rs232_pkt;
+ struct rs232_ctrl_msg *rs232_pkt = result->buf_addr;
struct uci_client *client = NULL;
struct mhi_uci_ctxt_t *uci_ctxt = ctrl_client->uci_ctxt;
u32 msg_id;
@@ -1094,7 +1051,6 @@ static void process_rs232_state(struct uci_client *ctrl_client,
sizeof(struct rs232_ctrl_msg));
goto error_size;
}
- rs232_pkt = result->buf_addr;
MHI_GET_CTRL_DEST_ID(CTRL_DEST_ID, rs232_pkt, chan);
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; i++)
if (chan == uci_ctxt->client_handles[i].out_attr.chan_id ||
diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c
index 9ea0b40304eb..0fec8acde96c 100644
--- a/drivers/platform/msm/qpnp-revid.c
+++ b/drivers/platform/msm/qpnp-revid.c
@@ -27,6 +27,7 @@
#define REVID_SUBTYPE 0x5
#define REVID_STATUS1 0x8
#define REVID_SPARE_0 0x60
+#define REVID_TP_REV 0xf1
#define REVID_FAB_ID 0xf2
#define QPNP_REVID_DEV_NAME "qcom,qpnp-revid"
@@ -157,9 +158,9 @@ static size_t build_pmic_string(char *buf, size_t n, int sid,
static int qpnp_revid_probe(struct platform_device *pdev)
{
u8 rev1, rev2, rev3, rev4, pmic_type, pmic_subtype, pmic_status;
- u8 option1, option2, option3, option4, spare0, fab_id;
+ u8 option1, option2, option3, option4, spare0;
unsigned int base;
- int rc;
+ int rc, fab_id, tp_rev;
char pmic_string[PMIC_STRING_MAXLENGTH] = {'\0'};
struct revid_chip *revid_chip;
struct regmap *regmap;
@@ -207,6 +208,11 @@ static int qpnp_revid_probe(struct platform_device *pdev)
else
fab_id = -EINVAL;
+ if (of_property_read_bool(pdev->dev.of_node, "qcom,tp-rev-valid"))
+ tp_rev = qpnp_read_byte(regmap, base + REVID_TP_REV);
+ else
+ tp_rev = -EINVAL;
+
revid_chip = devm_kzalloc(&pdev->dev, sizeof(struct revid_chip),
GFP_KERNEL);
if (!revid_chip)
@@ -220,6 +226,7 @@ static int qpnp_revid_probe(struct platform_device *pdev)
revid_chip->data.pmic_subtype = pmic_subtype;
revid_chip->data.pmic_type = pmic_type;
revid_chip->data.fab_id = fab_id;
+ revid_chip->data.tp_rev = tp_rev;
if (pmic_subtype < ARRAY_SIZE(pmic_names))
revid_chip->data.pmic_name = pmic_names[pmic_subtype];
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index ddb2388c5006..e7710f929e71 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -2322,8 +2322,11 @@ int sps_deregister_bam_device(unsigned long dev_handle)
mutex_lock(&bam->lock);
sps_bam_device_de_init(bam);
mutex_unlock(&bam->lock);
+ ipc_log_context_destroy(bam->ipc_log0);
ipc_log_context_destroy(bam->ipc_log1);
ipc_log_context_destroy(bam->ipc_log2);
+ ipc_log_context_destroy(bam->ipc_log3);
+ ipc_log_context_destroy(bam->ipc_log4);
if (bam->props.virt_size)
(void)iounmap(bam->props.virt_addr);
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index a151d0c7a770..a9bcabfdb009 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -103,7 +103,6 @@ struct usb_bam_sps_type {
struct usb_bam_ctx_type {
struct usb_bam_sps_type usb_bam_sps;
struct resource *io_res;
- void __iomem *regs;
int irq;
struct platform_device *usb_bam_pdev;
struct workqueue_struct *usb_bam_wq;
@@ -113,6 +112,7 @@ struct usb_bam_ctx_type {
u32 inactivity_timer_ms;
bool is_bam_inactivity;
struct usb_bam_pipe_connect *usb_bam_connections;
+ struct msm_usb_bam_data *usb_bam_data;
spinlock_t usb_bam_lock;
};
@@ -126,13 +126,13 @@ static char *bam_enable_strings[MAX_BAMS] = {
* CI_CTRL & DWC3_CTRL shouldn't be used simultaneously
* since both share the same prod & cons rm resourses
*/
-static enum ipa_client_type ipa_rm_resource_prod[MAX_BAMS] = {
+static enum ipa_rm_resource_name ipa_rm_resource_prod[MAX_BAMS] = {
[CI_CTRL] = IPA_RM_RESOURCE_USB_PROD,
[HSIC_CTRL] = IPA_RM_RESOURCE_HSIC_PROD,
[DWC3_CTRL] = IPA_RM_RESOURCE_USB_PROD,
};
-static enum ipa_client_type ipa_rm_resource_cons[MAX_BAMS] = {
+static enum ipa_rm_resource_name ipa_rm_resource_cons[MAX_BAMS] = {
[CI_CTRL] = IPA_RM_RESOURCE_USB_CONS,
[HSIC_CTRL] = IPA_RM_RESOURCE_HSIC_CONS,
[DWC3_CTRL] = IPA_RM_RESOURCE_USB_CONS,
@@ -235,10 +235,10 @@ void msm_bam_set_hsic_host_dev(struct device *dev)
if (dev) {
/* Hold the device until allowing lpm */
info[HSIC_CTRL].in_lpm = false;
- log_event_dbg("%s: Getting hsic device %p\n", __func__, dev);
+ log_event_dbg("%s: Getting hsic device %pK\n", __func__, dev);
pm_runtime_get(dev);
} else if (host_info[HSIC_CTRL].dev) {
- log_event_dbg("%s: Try Putting hsic device %p, lpm:%d\n",
+ log_event_dbg("%s: Try Putting hsic device %pK, lpm:%d\n",
__func__, host_info[HSIC_CTRL].dev,
info[HSIC_CTRL].in_lpm);
/* Just release previous device if not already done */
@@ -320,8 +320,6 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect)
{
int ret = 0;
struct usb_bam_ctx_type *ctx = &msm_usb_bam[pipe_connect->bam_type];
- struct msm_usb_bam_platform_data *pdata =
- ctx->usb_bam_pdev->dev.platform_data;
struct sps_mem_buffer *data_buf = &(pipe_connect->data_mem_buf);
struct sps_mem_buffer *desc_buf = &(pipe_connect->desc_mem_buf);
@@ -360,7 +358,7 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect)
}
data_buf->phys_base = pipe_connect->data_fifo_base_offset +
- pdata->usb_bam_fifo_baseaddr;
+ ctx->usb_bam_data->usb_bam_fifo_baseaddr;
data_buf->size = pipe_connect->data_fifo_size;
data_buf->base = ioremap(data_buf->phys_base, data_buf->size);
if (!data_buf->base) {
@@ -372,7 +370,7 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect)
memset_io(data_buf->base, 0, data_buf->size);
desc_buf->phys_base = pipe_connect->desc_fifo_base_offset +
- pdata->usb_bam_fifo_baseaddr;
+ ctx->usb_bam_data->usb_bam_fifo_baseaddr;
desc_buf->size = pipe_connect->desc_fifo_size;
desc_buf->base = ioremap(desc_buf->phys_base, desc_buf->size);
if (!desc_buf->base) {
@@ -825,7 +823,7 @@ static bool _hsic_host_bam_resume_core(void)
/* Exit from "full suspend" in case of hsic host */
if (host_info[HSIC_CTRL].dev && info[HSIC_CTRL].in_lpm) {
- log_event_dbg("%s: Getting hsic device %p\n", __func__,
+ log_event_dbg("%s: Getting hsic device %pK\n", __func__,
host_info[HSIC_CTRL].dev);
pm_runtime_get(host_info[HSIC_CTRL].dev);
info[HSIC_CTRL].in_lpm = false;
@@ -839,7 +837,7 @@ static void _hsic_host_bam_suspend_core(void)
log_event_dbg("%s: enter\n", __func__);
if (host_info[HSIC_CTRL].dev && !info[HSIC_CTRL].in_lpm) {
- log_event_dbg("%s: Putting hsic host device %p\n", __func__,
+ log_event_dbg("%s: Putting hsic host device %pK\n", __func__,
host_info[HSIC_CTRL].dev);
pm_runtime_put(host_info[HSIC_CTRL].dev);
info[HSIC_CTRL].in_lpm = true;
@@ -1070,7 +1068,6 @@ int usb_bam_connect(enum usb_ctrl cur_bam, int idx, u32 *bam_pipe_idx)
struct usb_bam_pipe_connect *pipe_connect =
&ctx->usb_bam_connections[idx];
struct device *bam_dev = &ctx->usb_bam_pdev->dev;
- struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data;
enum usb_bam_mode cur_mode;
if (pipe_connect->enabled) {
@@ -1096,7 +1093,7 @@ int usb_bam_connect(enum usb_ctrl cur_bam, int idx, u32 *bam_pipe_idx)
spin_lock(&ctx->usb_bam_lock);
/* Check if BAM requires RESET before connect and reset of first pipe */
- if ((pdata->reset_on_connect == true) &&
+ if ((ctx->usb_bam_data->reset_on_connect == true) &&
(ctx->pipes_enabled_per_bam == 0)) {
spin_unlock(&ctx->usb_bam_lock);
@@ -1596,8 +1593,6 @@ static int ss_usb_cons_release_resource(void)
static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam)
{
struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
- struct msm_usb_bam_platform_data *pdata =
- ctx->usb_bam_pdev->dev.platform_data;
struct ipa_rm_create_params usb_prod_create_params;
struct ipa_rm_create_params usb_cons_create_params;
int ret;
@@ -1606,7 +1601,8 @@ static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam)
memset(&usb_prod_create_params, 0, sizeof(usb_prod_create_params));
usb_prod_create_params.name = ipa_rm_resource_prod[cur_bam];
usb_prod_create_params.reg_params.notify_cb = usb_prod_notify_cb;
- usb_prod_create_params.reg_params.user_data = &pdata->bam_type;
+ usb_prod_create_params.reg_params.user_data
+ = &ctx->usb_bam_data->bam_type;
usb_prod_create_params.floor_voltage = IPA_VOLTAGE_SVS;
ret = ipa_rm_create_resource(&usb_prod_create_params);
if (ret) {
@@ -1629,6 +1625,22 @@ static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam)
}
}
+static void usb_bam_ipa_delete_resources(enum usb_ctrl cur_bam)
+{
+ int ret;
+
+ ret = ipa_rm_delete_resource(ipa_rm_resource_prod[cur_bam]);
+ if (ret)
+ log_event_err("%s: Failed to delete USB_PROD resource\n",
+ __func__);
+
+ ret = ipa_rm_delete_resource(ipa_rm_resource_cons[cur_bam]);
+ if (ret)
+ log_event_err("%s: Failed to delete USB_CONS resource\n",
+ __func__);
+
+}
+
static void wait_for_prod_granted(enum usb_ctrl cur_bam)
{
int ret;
@@ -1724,7 +1736,7 @@ static bool check_pipes_empty(enum usb_ctrl bam_type, u8 src_idx, u8 dst_idx)
/* If we have any remaints in the pipes we don't go to sleep */
prod_pipe = ctx->usb_bam_sps.sps_pipes[src_idx];
cons_pipe = ctx->usb_bam_sps.sps_pipes[dst_idx];
- log_event_dbg("prod_pipe=%p, cons_pipe=%p\n", prod_pipe, cons_pipe);
+ log_event_dbg("prod_pipe=%pK, cons_pipe=%pK\n", prod_pipe, cons_pipe);
if (!cons_pipe || (!prod_pipe &&
prod_pipe_connect->pipe_type == USB_BAM_PIPE_BAM2BAM)) {
@@ -2091,7 +2103,7 @@ static bool msm_bam_host_lpm_ok(enum usb_ctrl bam_type)
}
/* HSIC host will go now to lpm */
- log_event_dbg("%s: vote for suspend hsic %p\n",
+ log_event_dbg("%s: vote for suspend hsic %pK\n",
__func__, host_info[bam_type].dev);
for (i = 0; i < ctx->max_connections; i++) {
@@ -2136,16 +2148,14 @@ static int usb_bam_set_ipa_perf(enum usb_ctrl cur_bam,
int ret;
struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
struct ipa_rm_perf_profile ipa_rm_perf_prof;
- struct msm_usb_bam_platform_data *pdata =
- ctx->usb_bam_pdev->dev.platform_data;
if (usb_connection_speed == USB_SPEED_SUPER)
ipa_rm_perf_prof.max_supported_bandwidth_mbps =
- pdata->max_mbps_superspeed;
+ ctx->usb_bam_data->max_mbps_superspeed;
else
/* Bam2Bam is supported only for SS and HS (HW limitation) */
ipa_rm_perf_prof.max_supported_bandwidth_mbps =
- pdata->max_mbps_highspeed;
+ ctx->usb_bam_data->max_mbps_highspeed;
/*
* Having a max mbps property in dtsi file is a must
@@ -2182,7 +2192,6 @@ int usb_bam_connect_ipa(enum usb_ctrl cur_bam,
struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
struct usb_bam_pipe_connect *pipe_connect;
struct device *bam_dev = &ctx->usb_bam_pdev->dev;
- struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data;
int ret;
bool bam2bam, is_dpl;
@@ -2260,7 +2269,8 @@ int usb_bam_connect_ipa(enum usb_ctrl cur_bam,
/* Check if BAM requires RESET before connect and reset first pipe */
spin_lock(&ctx->usb_bam_lock);
- if (pdata->reset_on_connect && !ctx->pipes_enabled_per_bam) {
+ if (ctx->usb_bam_data->reset_on_connect &&
+ !ctx->pipes_enabled_per_bam) {
spin_unlock(&ctx->usb_bam_lock);
if (cur_bam == CI_CTRL)
@@ -2454,7 +2464,7 @@ static void usb_bam_work(struct work_struct *w)
if (pipe_iter->bam_type == pipe_connect->bam_type &&
pipe_iter->dir == PEER_PERIPHERAL_TO_USB &&
pipe_iter->enabled) {
- log_event_dbg("%s: Register wakeup on pipe %p\n",
+ log_event_dbg("%s: Register wakeup on pipe %pK\n",
__func__, pipe_iter);
__usb_bam_register_wake_cb(
pipe_connect->bam_type, i,
@@ -2610,8 +2620,6 @@ int usb_bam_disconnect_pipe(enum usb_ctrl bam_type, u8 idx)
struct usb_bam_pipe_connect *pipe_connect;
struct device *bam_dev = &ctx->usb_bam_pdev->dev;
int ret;
- struct msm_usb_bam_platform_data *pdata =
- ctx->usb_bam_pdev->dev.platform_data;
pipe_connect = &ctx->usb_bam_connections[idx];
@@ -2639,7 +2647,8 @@ int usb_bam_disconnect_pipe(enum usb_ctrl bam_type, u8 idx)
log_event_dbg("%s: success disconnecting pipe %d\n", __func__, idx);
- if (pdata->reset_on_disconnect && !ctx->pipes_enabled_per_bam) {
+ if (ctx->usb_bam_data->reset_on_disconnect
+ && !ctx->pipes_enabled_per_bam) {
if (bam_type == CI_CTRL)
msm_hw_bam_disable(1);
@@ -2807,10 +2816,10 @@ static void usb_bam_sps_events(enum sps_callback_case sps_cb_case, void *user)
}
}
-static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
+static struct msm_usb_bam_data *usb_bam_dt_to_data(
struct platform_device *pdev, u32 usb_addr)
{
- struct msm_usb_bam_platform_data *pdata;
+ struct msm_usb_bam_data *usb_bam_data;
struct device_node *node = pdev->dev.of_node;
int rc = 0;
u8 i = 0;
@@ -2819,10 +2828,10 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
u32 threshold, max_connections = 0;
static struct usb_bam_pipe_connect *usb_bam_connections;
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
+ usb_bam_data = devm_kzalloc(&pdev->dev, sizeof(*usb_bam_data),
+ GFP_KERNEL);
+ if (!usb_bam_data)
return NULL;
- }
rc = of_property_read_u32(node, "qcom,bam-type", &bam);
if (rc) {
@@ -2835,7 +2844,7 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
__func__, bam);
return NULL;
}
- pdata->bam_type = bam;
+ usb_bam_data->bam_type = bam;
rc = of_property_read_u32(node, "qcom,bam-mode", &bam_mode);
if (rc) {
@@ -2845,49 +2854,49 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
bam_mode = USB_BAM_DEVICE;
}
- pdata->reset_on_connect = of_property_read_bool(node,
+ usb_bam_data->reset_on_connect = of_property_read_bool(node,
"qcom,reset-bam-on-connect");
- pdata->reset_on_disconnect = of_property_read_bool(node,
+ usb_bam_data->reset_on_disconnect = of_property_read_bool(node,
"qcom,reset-bam-on-disconnect");
rc = of_property_read_u32(node, "qcom,usb-bam-num-pipes",
- &pdata->usb_bam_num_pipes);
+ &usb_bam_data->usb_bam_num_pipes);
if (rc) {
log_event_err("Invalid usb bam num pipes property\n");
return NULL;
}
rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-highspeed",
- &pdata->max_mbps_highspeed);
+ &usb_bam_data->max_mbps_highspeed);
if (rc)
- pdata->max_mbps_highspeed = 0;
+ usb_bam_data->max_mbps_highspeed = 0;
rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-superspeed",
- &pdata->max_mbps_superspeed);
+ &usb_bam_data->max_mbps_superspeed);
if (rc)
- pdata->max_mbps_superspeed = 0;
+ usb_bam_data->max_mbps_superspeed = 0;
rc = of_property_read_u32(node, "qcom,usb-bam-fifo-baseaddr", &addr);
if (rc)
pr_debug("%s: Invalid usb base address property\n", __func__);
else
- pdata->usb_bam_fifo_baseaddr = addr;
+ usb_bam_data->usb_bam_fifo_baseaddr = addr;
- pdata->ignore_core_reset_ack = of_property_read_bool(node,
+ usb_bam_data->ignore_core_reset_ack = of_property_read_bool(node,
"qcom,ignore-core-reset-ack");
- pdata->disable_clk_gating = of_property_read_bool(node,
+ usb_bam_data->disable_clk_gating = of_property_read_bool(node,
"qcom,disable-clk-gating");
rc = of_property_read_u32(node, "qcom,usb-bam-override-threshold",
&threshold);
if (rc)
- pdata->override_threshold = USB_THRESHOLD;
+ usb_bam_data->override_threshold = USB_THRESHOLD;
else
- pdata->override_threshold = threshold;
+ usb_bam_data->override_threshold = threshold;
- pdata->enable_hsusb_bam_on_boot = of_property_read_bool(node,
+ usb_bam_data->enable_hsusb_bam_on_boot = of_property_read_bool(node,
"qcom,enable-hsusb-bam-on-boot");
for_each_child_of_node(pdev->dev.of_node, node)
@@ -2923,7 +2932,7 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
goto err;
if (usb_bam_connections[i].mem_type == OCI_MEM) {
- if (!pdata->usb_bam_fifo_baseaddr) {
+ if (!usb_bam_data->usb_bam_fifo_baseaddr) {
log_event_err("%s: base address is missing\n",
__func__);
goto err;
@@ -3007,7 +3016,7 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
msm_usb_bam[bam].usb_bam_connections = usb_bam_connections;
msm_usb_bam[bam].max_connections = max_connections;
- return pdata;
+ return usb_bam_data;
err:
log_event_err("%s: failed\n", __func__);
return NULL;
@@ -3016,9 +3025,8 @@ err:
static int usb_bam_init(struct platform_device *pdev)
{
int ret;
- struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
- enum usb_ctrl bam_type = pdata->bam_type;
- struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+ struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+ enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type;
struct sps_bam_props props;
memset(&props, 0, sizeof(props));
@@ -3027,12 +3035,11 @@ static int usb_bam_init(struct platform_device *pdev)
bam_enable_strings[bam_type]);
props.phys_addr = ctx->io_res->start;
- props.virt_addr = ctx->regs;
props.virt_size = resource_size(ctx->io_res);
props.irq = ctx->irq;
- props.summing_threshold = pdata->override_threshold;
- props.event_threshold = pdata->override_threshold;
- props.num_pipes = pdata->usb_bam_num_pipes;
+ props.summing_threshold = ctx->usb_bam_data->override_threshold;
+ props.event_threshold = ctx->usb_bam_data->override_threshold;
+ props.num_pipes = ctx->usb_bam_data->usb_bam_num_pipes;
props.callback = usb_bam_sps_events;
props.user = bam_enable_strings[bam_type];
@@ -3040,10 +3047,10 @@ static int usb_bam_init(struct platform_device *pdev)
* HSUSB and HSIC Cores don't support RESET ACK signal to BAMs
* Hence, let BAM to ignore acknowledge from USB while resetting PIPE
*/
- if (pdata->ignore_core_reset_ack && bam_type != DWC3_CTRL)
+ if (ctx->usb_bam_data->ignore_core_reset_ack && bam_type != DWC3_CTRL)
props.options = SPS_BAM_NO_EXT_P_RST;
- if (pdata->disable_clk_gating)
+ if (ctx->usb_bam_data->disable_clk_gating)
props.options |= SPS_BAM_NO_LOCAL_CLK_GATING;
/*
@@ -3051,7 +3058,8 @@ static int usb_bam_init(struct platform_device *pdev)
* starting peripheral controller to avoid switching USB core mode
* from legacy to BAM with ongoing data transfers.
*/
- if (pdata->enable_hsusb_bam_on_boot && bam_type == CI_CTRL) {
+ if (ctx->usb_bam_data->enable_hsusb_bam_on_boot
+ && bam_type == CI_CTRL) {
pr_debug("Register and enable HSUSB BAM\n");
props.options |= SPS_BAM_OPT_ENABLE_AT_BOOT;
}
@@ -3068,9 +3076,8 @@ static int usb_bam_init(struct platform_device *pdev)
static int enable_usb_bam(struct platform_device *pdev)
{
int ret;
- struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
- enum usb_ctrl bam_type = pdata->bam_type;
- struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+ struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+ enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type;
ret = usb_bam_init(pdev);
if (ret) {
@@ -3137,14 +3144,19 @@ void usb_bam_register_panic_hdlr(void)
&usb_bam_panic_blk);
}
+static void usb_bam_unregister_panic_hdlr(void)
+{
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &usb_bam_panic_blk);
+}
+
static int usb_bam_probe(struct platform_device *pdev)
{
int ret, i, irq;
struct resource *io_res;
- void __iomem *regs;
enum usb_ctrl bam_type;
struct usb_bam_ctx_type *ctx;
- struct msm_usb_bam_platform_data *pdata;
+ struct msm_usb_bam_data *usb_bam_data;
dev_dbg(&pdev->dev, "usb_bam_probe\n");
@@ -3160,25 +3172,19 @@ static int usb_bam_probe(struct platform_device *pdev)
return irq;
}
- regs = devm_ioremap(&pdev->dev, io_res->start, resource_size(io_res));
- if (!regs) {
- log_event_err("%s: ioremap failed\n", __func__);
- return -ENOMEM;
- }
-
/* specify BAM physical address to be filled in BAM connections */
- pdata = usb_bam_dt_to_pdata(pdev, io_res->start);
- if (!pdata)
+ usb_bam_data = usb_bam_dt_to_data(pdev, io_res->start);
+ if (!usb_bam_data)
return -EINVAL;
- pdev->dev.platform_data = pdata;
- bam_type = pdata->bam_type;
+ bam_type = usb_bam_data->bam_type;
ctx = &msm_usb_bam[bam_type];
+ dev_set_drvdata(&pdev->dev, ctx);
ctx->usb_bam_pdev = pdev;
ctx->irq = irq;
- ctx->regs = regs;
ctx->io_res = io_res;
+ ctx->usb_bam_data = usb_bam_data;
for (i = 0; i < ctx->max_connections; i++) {
ctx->usb_bam_connections[i].enabled = 0;
@@ -3319,15 +3325,15 @@ EXPORT_SYMBOL(usb_bam_get_bam_type);
bool msm_usb_bam_enable(enum usb_ctrl bam, bool bam_enable)
{
- struct msm_usb_bam_platform_data *pdata;
+ struct msm_usb_bam_data *usb_bam_data;
struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam];
if (!ctx->usb_bam_pdev)
return 0;
- pdata = ctx->usb_bam_pdev->dev.platform_data;
+ usb_bam_data = ctx->usb_bam_data;
if ((bam != CI_CTRL) || !(bam_enable ||
- pdata->enable_hsusb_bam_on_boot))
+ usb_bam_data->enable_hsusb_bam_on_boot))
return 0;
msm_hw_bam_disable(1);
@@ -3409,10 +3415,11 @@ EXPORT_SYMBOL(msm_bam_hsic_lpm_ok);
static int usb_bam_remove(struct platform_device *pdev)
{
- struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
- enum usb_ctrl bam_type = pdata->bam_type;
- struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+ struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+ usb_bam_ipa_delete_resources(ctx->usb_bam_data->bam_type);
+ usb_bam_unregister_panic_hdlr();
+ sps_deregister_bam_device(ctx->h_bam);
destroy_workqueue(ctx->usb_bam_wq);
return 0;
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 0dd6017245fa..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),
@@ -297,6 +299,12 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(hw_current_max),
POWER_SUPPLY_ATTR(real_type),
POWER_SUPPLY_ATTR(pr_swap),
+ POWER_SUPPLY_ATTR(cc_step),
+ POWER_SUPPLY_ATTR(cc_step_sel),
+ POWER_SUPPLY_ATTR(sw_jeita_enabled),
+ POWER_SUPPLY_ATTR(pd_voltage_max),
+ POWER_SUPPLY_ATTR(pd_voltage_min),
+ POWER_SUPPLY_ATTR(sdp_current_max),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 947108c1410e..16441d6e09e4 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -49,7 +49,7 @@
#define SRAM_READ "fg_sram_read"
#define SRAM_WRITE "fg_sram_write"
#define PROFILE_LOAD "fg_profile_load"
-#define DELTA_SOC "fg_delta_soc"
+#define TTF_PRIMING "fg_ttf_priming"
/* Delta BSOC irq votable reasons */
#define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq"
@@ -81,6 +81,8 @@
#define BATT_THERM_NUM_COEFFS 3
+#define MAX_CC_STEPS 20
+
/* Debug flag definitions */
enum fg_debug_flag {
FG_IRQ = BIT(0), /* Show interrupts */
@@ -102,7 +104,7 @@ enum sram_access_flags {
};
/* JEITA */
-enum {
+enum jeita_levels {
JEITA_COLD = 0,
JEITA_COOL,
JEITA_WARM,
@@ -228,10 +230,16 @@ enum esr_timer_config {
NUM_ESR_TIMERS,
};
+enum ttf_mode {
+ TTF_MODE_NORMAL = 0,
+ TTF_MODE_QNOVO,
+};
+
/* DT parameters for FG device */
struct fg_dt_props {
bool force_load_profile;
bool hold_soc_while_full;
+ bool linearize_soc;
bool auto_recharge_soc;
int cutoff_volt_mv;
int empty_volt_mv;
@@ -309,16 +317,31 @@ struct fg_irq_info {
};
struct fg_circ_buf {
- int arr[20];
+ int arr[10];
int size;
int head;
};
+struct fg_cc_step_data {
+ int arr[MAX_CC_STEPS];
+ int sel;
+};
+
struct fg_pt {
s32 x;
s32 y;
};
+struct ttf {
+ struct fg_circ_buf ibatt;
+ struct fg_circ_buf vbatt;
+ struct fg_cc_step_data cc_step;
+ struct mutex lock;
+ int mode;
+ int last_ttf;
+ s64 last_ms;
+};
+
static const struct fg_pt fg_ln_table[] = {
{ 1000, 0 },
{ 2000, 693 },
@@ -358,6 +381,7 @@ struct fg_chip {
struct power_supply *usb_psy;
struct power_supply *dc_psy;
struct power_supply *parallel_psy;
+ struct power_supply *pc_port_psy;
struct iio_channel *batt_id_chan;
struct iio_channel *die_temp_chan;
struct fg_memif *sram;
@@ -374,9 +398,9 @@ struct fg_chip {
struct fg_cyc_ctr_data cyc_ctr;
struct notifier_block nb;
struct fg_cap_learning cl;
+ struct ttf ttf;
struct mutex bus_lock;
struct mutex sram_rw_lock;
- struct mutex batt_avg_lock;
struct mutex charge_full_lock;
u32 batt_soc_base;
u32 batt_info_base;
@@ -389,12 +413,14 @@ struct fg_chip {
int prev_charge_status;
int charge_done;
int charge_type;
+ int online_status;
int last_soc;
int last_batt_temp;
int health;
int maint_soc;
int delta_soc;
int last_msoc;
+ int last_recharge_volt_mv;
int esr_timer_charging_default[NUM_ESR_TIMERS];
enum slope_limit_status slope_limit_sts;
bool profile_available;
@@ -413,11 +439,8 @@ struct fg_chip {
struct completion soc_ready;
struct delayed_work profile_load_work;
struct work_struct status_change_work;
- struct work_struct cycle_count_work;
- struct delayed_work batt_avg_work;
+ struct delayed_work ttf_work;
struct delayed_work sram_dump_work;
- struct fg_circ_buf ibatt_circ_buf;
- struct fg_circ_buf vbatt_circ_buf;
};
/* Debugfs data structures are below */
@@ -475,5 +498,6 @@ extern bool is_qnovo_en(struct fg_chip *chip);
extern void fg_circ_buf_add(struct fg_circ_buf *, int);
extern void fg_circ_buf_clr(struct fg_circ_buf *);
extern int fg_circ_buf_avg(struct fg_circ_buf *, int *);
+extern int fg_circ_buf_median(struct fg_circ_buf *, int *);
extern int fg_lerp(const struct fg_pt *, size_t, s32, s32 *);
#endif
diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c
index 9635044e02a5..0cb1dea7113b 100644
--- a/drivers/power/supply/qcom/fg-util.c
+++ b/drivers/power/supply/qcom/fg-util.c
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include <linux/sort.h>
#include "fg-core.h"
void fg_circ_buf_add(struct fg_circ_buf *buf, int val)
@@ -39,6 +40,39 @@ int fg_circ_buf_avg(struct fg_circ_buf *buf, int *avg)
return 0;
}
+static int cmp_int(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+int fg_circ_buf_median(struct fg_circ_buf *buf, int *median)
+{
+ int *temp;
+
+ if (buf->size == 0)
+ return -ENODATA;
+
+ if (buf->size == 1) {
+ *median = buf->arr[0];
+ return 0;
+ }
+
+ temp = kmalloc_array(buf->size, sizeof(*temp), GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
+
+ memcpy(temp, buf->arr, buf->size * sizeof(*temp));
+ sort(temp, buf->size, sizeof(*temp), cmp_int, NULL);
+
+ if (buf->size % 2)
+ *median = temp[buf->size / 2];
+ else
+ *median = (temp[buf->size / 2 - 1] + temp[buf->size / 2]) / 2;
+
+ kfree(temp);
+ return 0;
+}
+
int fg_lerp(const struct fg_pt *pts, size_t tablesize, s32 input, s32 *output)
{
int i;
diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c
index b99558ed2100..3d0a71844608 100644
--- a/drivers/power/supply/qcom/pmic-voter.c
+++ b/drivers/power/supply/qcom/pmic-voter.c
@@ -438,12 +438,14 @@ out:
int rerun_election(struct votable *votable)
{
int rc = 0;
+ int effective_result;
lock_votable(votable);
+ effective_result = get_effective_result_locked(votable);
if (votable->callback)
rc = votable->callback(votable,
- votable->data,
- votable->effective_result,
+ votable->data,
+ effective_result,
get_client_str(votable, votable->effective_client_id));
unlock_votable(votable);
return rc;
@@ -519,11 +521,10 @@ static int show_votable_clients(struct seq_file *m, void *data)
lock_votable(votable);
- seq_printf(m, "Votable %s:\n", votable->name);
- seq_puts(m, "clients:\n");
for (i = 0; i < votable->num_clients; i++) {
if (votable->client_strs[i]) {
- seq_printf(m, "%-15s:\t\ten=%d\t\tv=%d\n",
+ seq_printf(m, "%s: %s:\t\t\ten=%d v=%d\n",
+ votable->name,
votable->client_strs[i],
votable->votes[i].enabled,
votable->votes[i].value);
@@ -542,11 +543,11 @@ static int show_votable_clients(struct seq_file *m, void *data)
break;
}
- seq_printf(m, "type: %s\n", type_str);
- seq_puts(m, "Effective:\n");
effective_client_str = get_effective_client_locked(votable);
- seq_printf(m, "%-15s:\t\tv=%d\n",
+ seq_printf(m, "%s: effective=%s type=%s v=%d\n",
+ votable->name,
effective_client_str ? effective_client_str : "none",
+ type_str,
get_effective_result_locked(votable));
unlock_votable(votable);
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index e03681ec13cc..4788053115e1 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -75,6 +75,8 @@
#define ESR_TIMER_CHG_MAX_OFFSET 0
#define ESR_TIMER_CHG_INIT_WORD 18
#define ESR_TIMER_CHG_INIT_OFFSET 2
+#define ESR_EXTRACTION_ENABLE_WORD 19
+#define ESR_EXTRACTION_ENABLE_OFFSET 0
#define PROFILE_LOAD_WORD 24
#define PROFILE_LOAD_OFFSET 0
#define ESR_RSLOW_DISCHG_WORD 34
@@ -574,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)
@@ -836,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;
@@ -976,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) {
@@ -1171,6 +1202,42 @@ static bool batt_psy_initialized(struct fg_chip *chip)
return true;
}
+static bool usb_psy_initialized(struct fg_chip *chip)
+{
+ if (chip->usb_psy)
+ return true;
+
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (!chip->usb_psy)
+ return false;
+
+ return true;
+}
+
+static bool pc_port_psy_initialized(struct fg_chip *chip)
+{
+ if (chip->pc_port_psy)
+ return true;
+
+ chip->pc_port_psy = power_supply_get_by_name("pc_port");
+ if (!chip->pc_port_psy)
+ return false;
+
+ return true;
+}
+
+static bool dc_psy_initialized(struct fg_chip *chip)
+{
+ if (chip->dc_psy)
+ return true;
+
+ chip->dc_psy = power_supply_get_by_name("dc");
+ if (!chip->dc_psy)
+ return false;
+
+ return true;
+}
+
static bool is_parallel_charger_available(struct fg_chip *chip)
{
if (!chip->parallel_psy)
@@ -1277,11 +1344,20 @@ static bool is_temp_valid_cap_learning(struct fg_chip *chip)
return true;
}
+#define QNOVO_CL_SKEW_DECIPCT -30
static void fg_cap_learning_post_process(struct fg_chip *chip)
{
int64_t max_inc_val, min_dec_val, old_cap;
int rc;
+ if (is_qnovo_en(chip)) {
+ fg_dbg(chip, FG_CAP_LEARN, "applying skew %d on current learnt capacity %lld\n",
+ QNOVO_CL_SKEW_DECIPCT, chip->cl.final_cc_uah);
+ chip->cl.final_cc_uah = chip->cl.final_cc_uah *
+ (1000 + QNOVO_CL_SKEW_DECIPCT);
+ do_div(chip->cl.final_cc_uah, 1000);
+ }
+
max_inc_val = chip->cl.learned_cc_uah
* (1000 + chip->dt.cl_max_cap_inc);
do_div(max_inc_val, 1000);
@@ -1590,6 +1666,9 @@ static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv)
if (chip->wa_flags & PMI8998_V1_REV_WA)
return 0;
+ if (voltage_mv == chip->last_recharge_volt_mv)
+ return 0;
+
fg_dbg(chip, FG_STATUS, "Setting recharge voltage to %dmV\n",
voltage_mv);
fg_encode(chip->sp, FG_SRAM_RECHARGE_VBATT_THR, voltage_mv, &buf);
@@ -1604,6 +1683,34 @@ static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv)
return rc;
}
+ chip->last_recharge_volt_mv = 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;
}
@@ -1611,8 +1718,7 @@ static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv)
static int fg_charge_full_update(struct fg_chip *chip)
{
union power_supply_propval prop = {0, };
- int rc, msoc, bsoc, recharge_soc;
- u8 full_soc[2] = {0xFF, 0xFF};
+ int rc, msoc, bsoc, recharge_soc, msoc_raw;
if (!chip->dt.hold_soc_while_full)
return 0;
@@ -1647,6 +1753,7 @@ static int fg_charge_full_update(struct fg_chip *chip)
pr_err("Error in getting msoc, rc=%d\n", rc);
goto out;
}
+ msoc_raw = DIV_ROUND_CLOSEST(msoc * FULL_SOC_RAW, FULL_CAPACITY);
fg_dbg(chip, FG_STATUS, "msoc: %d bsoc: %x health: %d status: %d full: %d\n",
msoc, bsoc, chip->health, chip->charge_status,
@@ -1670,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 ((bsoc >> 8) <= 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
@@ -1700,35 +1807,23 @@ static int fg_charge_full_update(struct fg_chip *chip)
rc);
goto out;
}
- fg_dbg(chip, FG_STATUS, "bsoc: %d recharge_soc: %d delta_soc: %d\n",
- bsoc >> 8, recharge_soc, chip->delta_soc);
- } 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;
@@ -1811,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];
@@ -1913,6 +2046,33 @@ static int fg_adjust_recharge_soc(struct fg_chip *chip)
return 0;
}
+static int fg_adjust_recharge_voltage(struct fg_chip *chip)
+{
+ int rc, recharge_volt_mv;
+
+ if (chip->dt.auto_recharge_soc)
+ return 0;
+
+ fg_dbg(chip, FG_STATUS, "health: %d chg_status: %d chg_done: %d\n",
+ chip->health, chip->charge_status, chip->charge_done);
+
+ recharge_volt_mv = chip->dt.recharge_volt_thr_mv;
+
+ /* Lower the recharge voltage in soft JEITA */
+ if (chip->health == POWER_SUPPLY_HEALTH_WARM ||
+ chip->health == POWER_SUPPLY_HEALTH_COOL)
+ recharge_volt_mv -= 200;
+
+ rc = fg_set_recharge_voltage(chip, recharge_volt_mv);
+ if (rc < 0) {
+ pr_err("Error in setting recharge_voltage, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
+
static int fg_slope_limit_config(struct fg_chip *chip, int batt_temp)
{
enum slope_limit_status status;
@@ -2130,102 +2290,67 @@ static int fg_esr_timer_config(struct fg_chip *chip, bool sleep)
return 0;
}
-static void fg_batt_avg_update(struct fg_chip *chip)
-{
- if (chip->charge_status == chip->prev_charge_status)
- return;
-
- cancel_delayed_work_sync(&chip->batt_avg_work);
- fg_circ_buf_clr(&chip->ibatt_circ_buf);
- fg_circ_buf_clr(&chip->vbatt_circ_buf);
-
- if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING ||
- chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING)
- schedule_delayed_work(&chip->batt_avg_work,
- msecs_to_jiffies(2000));
-}
-
-static void status_change_work(struct work_struct *work)
+static void fg_ttf_update(struct fg_chip *chip)
{
- struct fg_chip *chip = container_of(work,
- struct fg_chip, status_change_work);
+ int rc;
+ int delay_ms;
union power_supply_propval prop = {0, };
- int rc, batt_temp;
+ int online = 0;
- if (!batt_psy_initialized(chip)) {
- fg_dbg(chip, FG_STATUS, "Charger not available?!\n");
- goto out;
- }
+ if (usb_psy_initialized(chip)) {
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_ONLINE, &prop);
+ if (rc < 0) {
+ pr_err("Couldn't read usb ONLINE prop rc=%d\n", rc);
+ return;
+ }
- rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS,
- &prop);
- if (rc < 0) {
- pr_err("Error in getting charging status, rc=%d\n", rc);
- goto out;
+ online = online || prop.intval;
}
- chip->prev_charge_status = chip->charge_status;
- chip->charge_status = prop.intval;
- rc = power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
- if (rc < 0) {
- pr_err("Error in getting charge type, rc=%d\n", rc);
- goto out;
- }
+ if (pc_port_psy_initialized(chip)) {
+ rc = power_supply_get_property(chip->pc_port_psy,
+ POWER_SUPPLY_PROP_ONLINE, &prop);
+ if (rc < 0) {
+ pr_err("Couldn't read pc_port ONLINE prop rc=%d\n", rc);
+ return;
+ }
- chip->charge_type = prop.intval;
- rc = power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_CHARGE_DONE, &prop);
- if (rc < 0) {
- pr_err("Error in getting charge_done, rc=%d\n", rc);
- goto out;
+ online = online || prop.intval;
}
- chip->charge_done = prop.intval;
- if (chip->cyc_ctr.en)
- schedule_work(&chip->cycle_count_work);
-
- fg_cap_learning_update(chip);
-
- rc = fg_charge_full_update(chip);
- if (rc < 0)
- pr_err("Error in charge_full_update, rc=%d\n", rc);
-
- rc = fg_adjust_recharge_soc(chip);
- if (rc < 0)
- pr_err("Error in adjusting recharge_soc, rc=%d\n", rc);
-
- rc = fg_adjust_ki_coeff_dischg(chip);
- if (rc < 0)
- pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc);
-
- rc = fg_esr_fcc_config(chip);
- if (rc < 0)
- pr_err("Error in adjusting FCC for ESR, rc=%d\n", rc);
-
- rc = fg_esr_timer_config(chip, false);
- if (rc < 0)
- pr_err("Error in configuring ESR timer, rc=%d\n", rc);
-
- rc = fg_get_battery_temp(chip, &batt_temp);
- if (!rc) {
- rc = fg_slope_limit_config(chip, batt_temp);
- if (rc < 0)
- pr_err("Error in configuring slope limiter rc:%d\n",
- rc);
+ if (dc_psy_initialized(chip)) {
+ rc = power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_ONLINE, &prop);
+ if (rc < 0) {
+ pr_err("Couldn't read dc ONLINE prop rc=%d\n", rc);
+ return;
+ }
- rc = fg_adjust_ki_coeff_full_soc(chip, batt_temp);
- if (rc < 0)
- pr_err("Error in configuring ki_coeff_full_soc rc:%d\n",
- rc);
+ online = online || prop.intval;
}
- fg_batt_avg_update(chip);
-out:
- fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n",
- chip->charge_status, chip->charge_type, chip->charge_done);
- pm_relax(chip->dev);
+ if (chip->online_status == online)
+ return;
+
+ chip->online_status = online;
+ if (online)
+ /* wait 35 seconds for the input to settle */
+ delay_ms = 35000;
+ else
+ /* wait 5 seconds for current to settle during discharge */
+ delay_ms = 5000;
+
+ vote(chip->awake_votable, TTF_PRIMING, true, 0);
+ cancel_delayed_work_sync(&chip->ttf_work);
+ mutex_lock(&chip->ttf.lock);
+ fg_circ_buf_clr(&chip->ttf.ibatt);
+ fg_circ_buf_clr(&chip->ttf.vbatt);
+ chip->ttf.last_ttf = 0;
+ chip->ttf.last_ms = 0;
+ mutex_unlock(&chip->ttf.lock);
+ schedule_delayed_work(&chip->ttf_work, msecs_to_jiffies(delay_ms));
}
static void restore_cycle_counter(struct fg_chip *chip)
@@ -2233,6 +2358,9 @@ static void restore_cycle_counter(struct fg_chip *chip)
int rc = 0, i;
u8 data[2];
+ if (!chip->cyc_ctr.en)
+ return;
+
mutex_lock(&chip->cyc_ctr.lock);
for (i = 0; i < BUCKET_COUNT; i++) {
rc = fg_sram_read(chip, CYCLE_COUNT_WORD + (i / 2),
@@ -2286,20 +2414,25 @@ static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket)
rc = fg_sram_write(chip, CYCLE_COUNT_WORD + (bucket / 2),
CYCLE_COUNT_OFFSET + (bucket % 2) * 2, data, 2,
FG_IMA_DEFAULT);
- if (rc < 0)
+ if (rc < 0) {
pr_err("failed to write BATT_CYCLE[%d] rc=%d\n",
bucket, rc);
- else
- chip->cyc_ctr.count[bucket] = cyc_count;
+ return rc;
+ }
+
+ chip->cyc_ctr.count[bucket] = cyc_count;
+ fg_dbg(chip, FG_STATUS, "Stored count %d in bucket %d\n", cyc_count,
+ bucket);
+
return rc;
}
-static void cycle_count_work(struct work_struct *work)
+static void fg_cycle_counter_update(struct fg_chip *chip)
{
int rc = 0, bucket, i, batt_soc;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- cycle_count_work);
+
+ if (!chip->cyc_ctr.en)
+ return;
mutex_lock(&chip->cyc_ctr.lock);
rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &batt_soc);
@@ -2311,45 +2444,30 @@ static void cycle_count_work(struct work_struct *work)
/* We need only the most significant byte here */
batt_soc = (u32)batt_soc >> 24;
- if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) {
- /* Find out which bucket the SOC falls in */
- bucket = batt_soc / BUCKET_SOC_PCT;
- pr_debug("batt_soc: %d bucket: %d\n", batt_soc, bucket);
+ /* Find out which bucket the SOC falls in */
+ bucket = batt_soc / BUCKET_SOC_PCT;
- /*
- * If we've started counting for the previous bucket,
- * then store the counter for that bucket if the
- * counter for current bucket is getting started.
- */
- if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] &&
- !chip->cyc_ctr.started[bucket]) {
- rc = fg_inc_store_cycle_ctr(chip, bucket - 1);
- if (rc < 0) {
- pr_err("Error in storing cycle_ctr rc: %d\n",
- rc);
- goto out;
- } else {
- chip->cyc_ctr.started[bucket - 1] = false;
- chip->cyc_ctr.last_soc[bucket - 1] = 0;
- }
- }
+ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) {
if (!chip->cyc_ctr.started[bucket]) {
chip->cyc_ctr.started[bucket] = true;
chip->cyc_ctr.last_soc[bucket] = batt_soc;
}
- } else {
+ } else if (chip->charge_done || !is_input_present(chip)) {
for (i = 0; i < BUCKET_COUNT; i++) {
if (chip->cyc_ctr.started[i] &&
- batt_soc > chip->cyc_ctr.last_soc[i]) {
+ batt_soc > chip->cyc_ctr.last_soc[i] + 2) {
rc = fg_inc_store_cycle_ctr(chip, i);
if (rc < 0)
pr_err("Error in storing cycle_ctr rc: %d\n",
rc);
chip->cyc_ctr.last_soc[i] = 0;
+ chip->cyc_ctr.started[i] = false;
}
- chip->cyc_ctr.started[i] = false;
}
}
+
+ fg_dbg(chip, FG_STATUS, "batt_soc: %d bucket: %d chg_status: %d\n",
+ batt_soc, bucket, chip->charge_status);
out:
mutex_unlock(&chip->cyc_ctr.lock);
}
@@ -2370,6 +2488,87 @@ static int fg_get_cycle_count(struct fg_chip *chip)
return count;
}
+static void status_change_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip, status_change_work);
+ union power_supply_propval prop = {0, };
+ int rc, batt_temp;
+
+ if (!batt_psy_initialized(chip)) {
+ fg_dbg(chip, FG_STATUS, "Charger not available?!\n");
+ goto out;
+ }
+
+ rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS,
+ &prop);
+ if (rc < 0) {
+ pr_err("Error in getting charging status, rc=%d\n", rc);
+ goto out;
+ }
+
+ chip->prev_charge_status = chip->charge_status;
+ chip->charge_status = prop.intval;
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
+ if (rc < 0) {
+ pr_err("Error in getting charge type, rc=%d\n", rc);
+ goto out;
+ }
+
+ chip->charge_type = prop.intval;
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_DONE, &prop);
+ if (rc < 0) {
+ pr_err("Error in getting charge_done, rc=%d\n", rc);
+ goto out;
+ }
+
+ chip->charge_done = prop.intval;
+ fg_cycle_counter_update(chip);
+ fg_cap_learning_update(chip);
+
+ rc = fg_charge_full_update(chip);
+ if (rc < 0)
+ pr_err("Error in charge_full_update, rc=%d\n", rc);
+
+ rc = fg_adjust_recharge_soc(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting recharge_soc, rc=%d\n", rc);
+
+ rc = fg_adjust_recharge_voltage(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting recharge_voltage, rc=%d\n", rc);
+
+ rc = fg_adjust_ki_coeff_dischg(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc);
+
+ rc = fg_esr_fcc_config(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting FCC for ESR, rc=%d\n", rc);
+
+ rc = fg_get_battery_temp(chip, &batt_temp);
+ if (!rc) {
+ rc = fg_slope_limit_config(chip, batt_temp);
+ if (rc < 0)
+ pr_err("Error in configuring slope limiter rc:%d\n",
+ rc);
+
+ rc = fg_adjust_ki_coeff_full_soc(chip, batt_temp);
+ if (rc < 0)
+ pr_err("Error in configuring ki_coeff_full_soc rc:%d\n",
+ rc);
+ }
+
+ fg_ttf_update(chip);
+
+out:
+ fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n",
+ chip->charge_status, chip->charge_type, chip->charge_done);
+ pm_relax(chip->dev);
+}
+
static int fg_bp_params_config(struct fg_chip *chip)
{
int rc = 0;
@@ -2730,45 +2929,19 @@ static struct kernel_param_ops fg_restart_ops = {
module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644);
-#define BATT_AVG_POLL_PERIOD_MS 10000
-static void batt_avg_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work, struct fg_chip,
- batt_avg_work.work);
- int rc, ibatt_now, vbatt_now;
-
- mutex_lock(&chip->batt_avg_lock);
- rc = fg_get_battery_current(chip, &ibatt_now);
- if (rc < 0) {
- pr_err("failed to get battery current, rc=%d\n", rc);
- goto reschedule;
- }
-
- rc = fg_get_battery_voltage(chip, &vbatt_now);
- if (rc < 0) {
- pr_err("failed to get battery voltage, rc=%d\n", rc);
- goto reschedule;
- }
-
- fg_circ_buf_add(&chip->ibatt_circ_buf, ibatt_now);
- fg_circ_buf_add(&chip->vbatt_circ_buf, vbatt_now);
-
-reschedule:
- mutex_unlock(&chip->batt_avg_lock);
- schedule_delayed_work(&chip->batt_avg_work,
- msecs_to_jiffies(BATT_AVG_POLL_PERIOD_MS));
-}
-
#define HOURS_TO_SECONDS 3600
#define OCV_SLOPE_UV 10869
#define MILLI_UNIT 1000
#define MICRO_UNIT 1000000
-static int fg_get_time_to_full(struct fg_chip *chip, int *val)
+#define NANO_UNIT 1000000000
+static int fg_get_time_to_full_locked(struct fg_chip *chip, int *val)
{
- int rc, ibatt_avg, vbatt_avg, rbatt, msoc, ocv_cc2cv, full_soc,
- act_cap_uah;
- s32 i_cc2cv, soc_cc2cv, ln_val, centi_tau_scale;
- s64 t_predicted_cc = 0, t_predicted_cv = 0;
+ int rc, ibatt_avg, vbatt_avg, rbatt, msoc, full_soc, act_cap_mah,
+ i_cc2cv, soc_cc2cv, tau, divisor, iterm, ttf_mode,
+ i, soc_per_step, msoc_this_step, msoc_next_step,
+ ibatt_this_step, t_predicted_this_step, ttf_slope,
+ t_predicted_cv, t_predicted = 0;
+ s64 delta_ms;
if (chip->bp.float_volt_uv <= 0) {
pr_err("battery profile is not loaded\n");
@@ -2787,48 +2960,53 @@ static int fg_get_time_to_full(struct fg_chip *chip, int *val)
}
fg_dbg(chip, FG_TTF, "msoc=%d\n", msoc);
+ /* the battery is considered full if the SOC is 100% */
if (msoc >= 100) {
*val = 0;
return 0;
}
- mutex_lock(&chip->batt_avg_lock);
- rc = fg_circ_buf_avg(&chip->ibatt_circ_buf, &ibatt_avg);
- if (rc < 0) {
- /* try to get instantaneous current */
- rc = fg_get_battery_current(chip, &ibatt_avg);
- if (rc < 0) {
- mutex_unlock(&chip->batt_avg_lock);
- pr_err("failed to get battery current, rc=%d\n", rc);
- return rc;
- }
+ if (is_qnovo_en(chip))
+ ttf_mode = TTF_MODE_QNOVO;
+ else
+ ttf_mode = TTF_MODE_NORMAL;
+
+ /* when switching TTF algorithms the TTF needs to be reset */
+ if (chip->ttf.mode != ttf_mode) {
+ fg_circ_buf_clr(&chip->ttf.ibatt);
+ fg_circ_buf_clr(&chip->ttf.vbatt);
+ chip->ttf.last_ttf = 0;
+ chip->ttf.last_ms = 0;
+ chip->ttf.mode = ttf_mode;
}
- rc = fg_circ_buf_avg(&chip->vbatt_circ_buf, &vbatt_avg);
+ /* at least 10 samples are required to produce a stable IBATT */
+ if (chip->ttf.ibatt.size < 10) {
+ *val = -1;
+ return 0;
+ }
+
+ rc = fg_circ_buf_median(&chip->ttf.ibatt, &ibatt_avg);
if (rc < 0) {
- /* try to get instantaneous voltage */
- rc = fg_get_battery_voltage(chip, &vbatt_avg);
- if (rc < 0) {
- mutex_unlock(&chip->batt_avg_lock);
- pr_err("failed to get battery voltage, rc=%d\n", rc);
- return rc;
- }
+ pr_err("failed to get IBATT AVG rc=%d\n", rc);
+ return rc;
}
- mutex_unlock(&chip->batt_avg_lock);
- fg_dbg(chip, FG_TTF, "vbatt_avg=%d\n", vbatt_avg);
+ rc = fg_circ_buf_median(&chip->ttf.vbatt, &vbatt_avg);
+ if (rc < 0) {
+ pr_err("failed to get VBATT AVG rc=%d\n", rc);
+ return rc;
+ }
- /* clamp ibatt_avg to -150mA */
- if (ibatt_avg > -150000)
- ibatt_avg = -150000;
- fg_dbg(chip, FG_TTF, "ibatt_avg=%d\n", ibatt_avg);
+ ibatt_avg = -ibatt_avg / MILLI_UNIT;
+ vbatt_avg /= MILLI_UNIT;
- /* reverse polarity to be consistent with unsigned current settings */
- ibatt_avg = abs(ibatt_avg);
+ /* clamp ibatt_avg to iterm */
+ if (ibatt_avg < abs(chip->dt.sys_term_curr_ma))
+ ibatt_avg = abs(chip->dt.sys_term_curr_ma);
- /* estimated battery current at the CC to CV transition */
- i_cc2cv = div_s64((s64)ibatt_avg * vbatt_avg, chip->bp.float_volt_uv);
- fg_dbg(chip, FG_TTF, "i_cc2cv=%d\n", i_cc2cv);
+ fg_dbg(chip, FG_TTF, "ibatt_avg=%d\n", ibatt_avg);
+ fg_dbg(chip, FG_TTF, "vbatt_avg=%d\n", vbatt_avg);
rc = fg_get_battery_resistance(chip, &rbatt);
if (rc < 0) {
@@ -2836,19 +3014,14 @@ static int fg_get_time_to_full(struct fg_chip *chip, int *val)
return rc;
}
- /* clamp rbatt to 50mOhms */
- if (rbatt < 50000)
- rbatt = 50000;
-
+ rbatt /= MILLI_UNIT;
fg_dbg(chip, FG_TTF, "rbatt=%d\n", rbatt);
- rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_uah);
+ rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_mah);
if (rc < 0) {
pr_err("failed to get ACT_BATT_CAP rc=%d\n", rc);
return rc;
}
- act_cap_uah *= MILLI_UNIT;
- fg_dbg(chip, FG_TTF, "actual_capacity_uah=%d\n", act_cap_uah);
rc = fg_get_sram_prop(chip, FG_SRAM_FULL_SOC, &full_soc);
if (rc < 0) {
@@ -2857,69 +3030,148 @@ static int fg_get_time_to_full(struct fg_chip *chip, int *val)
}
full_soc = DIV_ROUND_CLOSEST(((u16)full_soc >> 8) * FULL_CAPACITY,
FULL_SOC_RAW);
- fg_dbg(chip, FG_TTF, "full_soc=%d\n", full_soc);
+ act_cap_mah = full_soc * act_cap_mah / 100;
+ fg_dbg(chip, FG_TTF, "act_cap_mah=%d\n", act_cap_mah);
+
+ /* estimated battery current at the CC to CV transition */
+ switch (chip->ttf.mode) {
+ case TTF_MODE_NORMAL:
+ i_cc2cv = ibatt_avg * vbatt_avg /
+ max(MILLI_UNIT, chip->bp.float_volt_uv / MILLI_UNIT);
+ break;
+ case TTF_MODE_QNOVO:
+ i_cc2cv = min(
+ chip->ttf.cc_step.arr[MAX_CC_STEPS - 1] / MILLI_UNIT,
+ ibatt_avg * vbatt_avg /
+ max(MILLI_UNIT, chip->bp.float_volt_uv / MILLI_UNIT));
+ break;
+ default:
+ pr_err("TTF mode %d is not supported\n", chip->ttf.mode);
+ break;
+ }
+ fg_dbg(chip, FG_TTF, "i_cc2cv=%d\n", i_cc2cv);
/* if we are already in CV state then we can skip estimating CC */
if (chip->charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER)
- goto skip_cc_estimate;
-
- /* if the charger is current limited then use power approximation */
- if (ibatt_avg > chip->bp.fastchg_curr_ma * MILLI_UNIT - 50000)
- ocv_cc2cv = div_s64((s64)rbatt * ibatt_avg, MICRO_UNIT);
- else
- ocv_cc2cv = div_s64((s64)rbatt * i_cc2cv, MICRO_UNIT);
- ocv_cc2cv = chip->bp.float_volt_uv - ocv_cc2cv;
- fg_dbg(chip, FG_TTF, "ocv_cc2cv=%d\n", ocv_cc2cv);
+ goto cv_estimate;
- soc_cc2cv = div_s64(chip->bp.float_volt_uv - ocv_cc2cv, OCV_SLOPE_UV);
/* estimated SOC at the CC to CV transition */
+ soc_cc2cv = DIV_ROUND_CLOSEST(rbatt * i_cc2cv, OCV_SLOPE_UV);
soc_cc2cv = 100 - soc_cc2cv;
fg_dbg(chip, FG_TTF, "soc_cc2cv=%d\n", soc_cc2cv);
- /* the esimated SOC may be lower than the current SOC */
- if (soc_cc2cv - msoc <= 0)
- goto skip_cc_estimate;
+ switch (chip->ttf.mode) {
+ case TTF_MODE_NORMAL:
+ if (soc_cc2cv - msoc <= 0)
+ goto cv_estimate;
+
+ divisor = max(100, (ibatt_avg + i_cc2cv) / 2 * 100);
+ t_predicted = div_s64((s64)act_cap_mah * (soc_cc2cv - msoc) *
+ HOURS_TO_SECONDS, divisor);
+ break;
+ case TTF_MODE_QNOVO:
+ soc_per_step = 100 / MAX_CC_STEPS;
+ for (i = msoc / soc_per_step; i < MAX_CC_STEPS - 1; ++i) {
+ msoc_next_step = (i + 1) * soc_per_step;
+ if (i == msoc / soc_per_step)
+ msoc_this_step = msoc;
+ else
+ msoc_this_step = i * soc_per_step;
+
+ /* scale ibatt by 85% to account for discharge pulses */
+ ibatt_this_step = min(
+ chip->ttf.cc_step.arr[i] / MILLI_UNIT,
+ ibatt_avg) * 85 / 100;
+ divisor = max(100, ibatt_this_step * 100);
+ t_predicted_this_step = div_s64((s64)act_cap_mah *
+ (msoc_next_step - msoc_this_step) *
+ HOURS_TO_SECONDS, divisor);
+ t_predicted += t_predicted_this_step;
+ fg_dbg(chip, FG_TTF, "[%d, %d] ma=%d t=%d\n",
+ msoc_this_step, msoc_next_step,
+ ibatt_this_step, t_predicted_this_step);
+ }
+ break;
+ default:
+ pr_err("TTF mode %d is not supported\n", chip->ttf.mode);
+ break;
+ }
- t_predicted_cc = div_s64((s64)full_soc * act_cap_uah, 100);
- t_predicted_cc = div_s64(t_predicted_cc * (soc_cc2cv - msoc), 100);
- t_predicted_cc *= HOURS_TO_SECONDS;
- t_predicted_cc = div_s64(t_predicted_cc, (ibatt_avg + i_cc2cv) / 2);
+cv_estimate:
+ fg_dbg(chip, FG_TTF, "t_predicted_cc=%d\n", t_predicted);
-skip_cc_estimate:
- fg_dbg(chip, FG_TTF, "t_predicted_cc=%lld\n", t_predicted_cc);
+ iterm = max(100, abs(chip->dt.sys_term_curr_ma) + 200);
+ fg_dbg(chip, FG_TTF, "iterm=%d\n", iterm);
- /* CV estimate starts here */
- if (chip->charge_type >= POWER_SUPPLY_CHARGE_TYPE_TAPER)
- ln_val = ibatt_avg / (abs(chip->dt.sys_term_curr_ma) + 200);
+ if (chip->charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER)
+ tau = max(MILLI_UNIT, ibatt_avg * MILLI_UNIT / iterm);
else
- ln_val = i_cc2cv / (abs(chip->dt.sys_term_curr_ma) + 200);
+ tau = max(MILLI_UNIT, i_cc2cv * MILLI_UNIT / iterm);
- if (msoc < 95)
- centi_tau_scale = 100;
- else
- centi_tau_scale = 20 * (100 - msoc);
-
- fg_dbg(chip, FG_TTF, "ln_in=%d\n", ln_val);
- rc = fg_lerp(fg_ln_table, ARRAY_SIZE(fg_ln_table), ln_val, &ln_val);
- fg_dbg(chip, FG_TTF, "ln_out=%d\n", ln_val);
- t_predicted_cv = div_s64((s64)act_cap_uah * rbatt, MICRO_UNIT);
- t_predicted_cv = div_s64(t_predicted_cv * centi_tau_scale, 100);
- t_predicted_cv = div_s64(t_predicted_cv * ln_val, MILLI_UNIT);
- t_predicted_cv = div_s64(t_predicted_cv * HOURS_TO_SECONDS, MICRO_UNIT);
- fg_dbg(chip, FG_TTF, "t_predicted_cv=%lld\n", t_predicted_cv);
- *val = t_predicted_cc + t_predicted_cv;
+ rc = fg_lerp(fg_ln_table, ARRAY_SIZE(fg_ln_table), tau, &tau);
+ if (rc < 0) {
+ pr_err("failed to interpolate tau rc=%d\n", rc);
+ return rc;
+ }
+
+ /* tau is scaled linearly from 95% to 100% SOC */
+ if (msoc >= 95)
+ tau = tau * 2 * (100 - msoc) / 10;
+
+ fg_dbg(chip, FG_TTF, "tau=%d\n", tau);
+ t_predicted_cv = div_s64((s64)act_cap_mah * rbatt * tau *
+ HOURS_TO_SECONDS, NANO_UNIT);
+ fg_dbg(chip, FG_TTF, "t_predicted_cv=%d\n", t_predicted_cv);
+ t_predicted += t_predicted_cv;
+
+ fg_dbg(chip, FG_TTF, "t_predicted_prefilter=%d\n", t_predicted);
+ if (chip->ttf.last_ms != 0) {
+ delta_ms = ktime_ms_delta(ktime_get_boottime(),
+ ms_to_ktime(chip->ttf.last_ms));
+ if (delta_ms > 10000) {
+ ttf_slope = div64_s64(
+ (s64)(t_predicted - chip->ttf.last_ttf) *
+ MICRO_UNIT, delta_ms);
+ if (ttf_slope > -100)
+ ttf_slope = -100;
+ else if (ttf_slope < -2000)
+ ttf_slope = -2000;
+
+ t_predicted = div_s64(
+ (s64)ttf_slope * delta_ms, MICRO_UNIT) +
+ chip->ttf.last_ttf;
+ fg_dbg(chip, FG_TTF, "ttf_slope=%d\n", ttf_slope);
+ } else {
+ t_predicted = chip->ttf.last_ttf;
+ }
+ }
+
+ /* clamp the ttf to 0 */
+ if (t_predicted < 0)
+ t_predicted = 0;
+
+ fg_dbg(chip, FG_TTF, "t_predicted_postfilter=%d\n", t_predicted);
+ *val = t_predicted;
return 0;
}
+static int fg_get_time_to_full(struct fg_chip *chip, int *val)
+{
+ int rc;
+
+ mutex_lock(&chip->ttf.lock);
+ rc = fg_get_time_to_full_locked(chip, val);
+ mutex_unlock(&chip->ttf.lock);
+ return rc;
+}
+
#define CENTI_ICORRECT_C0 105
#define CENTI_ICORRECT_C1 20
static int fg_get_time_to_empty(struct fg_chip *chip, int *val)
{
- int rc, ibatt_avg, msoc, act_cap_uah;
- s32 divisor;
- s64 t_predicted;
+ int rc, ibatt_avg, msoc, full_soc, act_cap_mah, divisor;
- rc = fg_circ_buf_avg(&chip->ibatt_circ_buf, &ibatt_avg);
+ rc = fg_circ_buf_median(&chip->ttf.ibatt, &ibatt_avg);
if (rc < 0) {
/* try to get instantaneous current */
rc = fg_get_battery_current(chip, &ibatt_avg);
@@ -2929,31 +3181,36 @@ static int fg_get_time_to_empty(struct fg_chip *chip, int *val)
}
}
- /* clamp ibatt_avg to 150mA */
- if (ibatt_avg < 150000)
- ibatt_avg = 150000;
+ ibatt_avg /= MILLI_UNIT;
+ /* clamp ibatt_avg to 100mA */
+ if (ibatt_avg < 100)
+ ibatt_avg = 100;
+
+ rc = fg_get_prop_capacity(chip, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting capacity, rc=%d\n", rc);
+ return rc;
+ }
- rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_uah);
+ rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_mah);
if (rc < 0) {
pr_err("Error in getting ACT_BATT_CAP, rc=%d\n", rc);
return rc;
}
- act_cap_uah *= MILLI_UNIT;
- rc = fg_get_prop_capacity(chip, &msoc);
+ rc = fg_get_sram_prop(chip, FG_SRAM_FULL_SOC, &full_soc);
if (rc < 0) {
- pr_err("Error in getting capacity, rc=%d\n", rc);
+ pr_err("failed to get full soc rc=%d\n", rc);
return rc;
}
+ full_soc = DIV_ROUND_CLOSEST(((u16)full_soc >> 8) * FULL_CAPACITY,
+ FULL_SOC_RAW);
+ act_cap_mah = full_soc * act_cap_mah / 100;
- t_predicted = div_s64((s64)msoc * act_cap_uah, 100);
- t_predicted *= HOURS_TO_SECONDS;
divisor = CENTI_ICORRECT_C0 * 100 + CENTI_ICORRECT_C1 * msoc;
- divisor = div_s64((s64)divisor * ibatt_avg, 10000);
- if (divisor > 0)
- t_predicted = div_s64(t_predicted, divisor);
-
- *val = t_predicted;
+ divisor = ibatt_avg * divisor / 100;
+ divisor = max(100, divisor);
+ *val = act_cap_mah * msoc * HOURS_TO_SECONDS / divisor;
return 0;
}
@@ -2961,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;
@@ -3032,6 +3292,150 @@ static int fg_esr_validate(struct fg_chip *chip)
return 0;
}
+static int fg_force_esr_meas(struct fg_chip *chip)
+{
+ int rc;
+ int esr_uohms;
+
+ /* force esr extraction enable */
+ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
+ ESR_EXTRACTION_ENABLE_OFFSET, BIT(0), BIT(0),
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("failed to enable esr extn rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip),
+ LD_REG_CTRL_BIT, 0);
+ if (rc < 0) {
+ pr_err("Error in configuring qnovo_cfg rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_masked_write(chip, BATT_INFO_TM_MISC1(chip),
+ ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT,
+ ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT);
+ if (rc < 0) {
+ pr_err("Error in configuring force ESR rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait 1.5 seconds for hw to measure ESR */
+ msleep(1500);
+ rc = fg_masked_write(chip, BATT_INFO_TM_MISC1(chip),
+ ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT,
+ 0);
+ if (rc < 0) {
+ pr_err("Error in restoring force ESR rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip),
+ LD_REG_CTRL_BIT, LD_REG_CTRL_BIT);
+ if (rc < 0) {
+ pr_err("Error in restoring qnovo_cfg rc=%d\n", rc);
+ return rc;
+ }
+
+ /* force esr extraction disable */
+ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
+ ESR_EXTRACTION_ENABLE_OFFSET, BIT(0), 0,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("failed to disable esr extn rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_get_battery_resistance(chip, &esr_uohms);
+ fg_dbg(chip, FG_STATUS, "ESR uohms = %d\n", esr_uohms);
+
+ return rc;
+}
+
+static int fg_prepare_for_qnovo(struct fg_chip *chip, int qnovo_enable)
+{
+ int rc;
+
+ /* force esr extraction disable when qnovo enables */
+ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
+ ESR_EXTRACTION_ENABLE_OFFSET,
+ BIT(0), qnovo_enable ? 0 : BIT(0),
+ FG_IMA_DEFAULT);
+ if (rc < 0)
+ pr_err("Error in configuring esr extraction rc=%d\n", rc);
+
+ rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip),
+ LD_REG_CTRL_BIT,
+ qnovo_enable ? LD_REG_CTRL_BIT : 0);
+ if (rc < 0) {
+ pr_err("Error in configuring qnovo_cfg rc=%d\n", rc);
+ return rc;
+ }
+ fg_dbg(chip, FG_STATUS, "Prepared for Qnovo\n");
+ return 0;
+}
+
+static void ttf_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work, struct fg_chip,
+ ttf_work.work);
+ int rc, ibatt_now, vbatt_now, ttf;
+ ktime_t ktime_now;
+
+ mutex_lock(&chip->ttf.lock);
+ if (chip->charge_status != POWER_SUPPLY_STATUS_CHARGING &&
+ chip->charge_status != POWER_SUPPLY_STATUS_DISCHARGING)
+ goto end_work;
+
+ rc = fg_get_battery_current(chip, &ibatt_now);
+ if (rc < 0) {
+ pr_err("failed to get battery current, rc=%d\n", rc);
+ goto end_work;
+ }
+
+ rc = fg_get_battery_voltage(chip, &vbatt_now);
+ if (rc < 0) {
+ pr_err("failed to get battery voltage, rc=%d\n", rc);
+ goto end_work;
+ }
+
+ fg_circ_buf_add(&chip->ttf.ibatt, ibatt_now);
+ fg_circ_buf_add(&chip->ttf.vbatt, vbatt_now);
+
+ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) {
+ rc = fg_get_time_to_full_locked(chip, &ttf);
+ if (rc < 0) {
+ pr_err("failed to get ttf, rc=%d\n", rc);
+ goto end_work;
+ }
+
+ /* keep the wake lock and prime the IBATT and VBATT buffers */
+ if (ttf < 0) {
+ /* delay for one FG cycle */
+ schedule_delayed_work(&chip->ttf_work,
+ msecs_to_jiffies(1500));
+ mutex_unlock(&chip->ttf.lock);
+ return;
+ }
+
+ /* update the TTF reference point every minute */
+ ktime_now = ktime_get_boottime();
+ if (ktime_ms_delta(ktime_now,
+ ms_to_ktime(chip->ttf.last_ms)) > 60000 ||
+ chip->ttf.last_ms == 0) {
+ chip->ttf.last_ttf = ttf;
+ chip->ttf.last_ms = ktime_to_ms(ktime_now);
+ }
+ }
+
+ /* recurse every 10 seconds */
+ schedule_delayed_work(&chip->ttf_work, msecs_to_jiffies(10000));
+end_work:
+ vote(chip->awake_votable, TTF_PRIMING, false, 0);
+ mutex_unlock(&chip->ttf.lock);
+}
+
/* PSY CALLBACKS STAY HERE */
static int fg_psy_get_property(struct power_supply *psy,
@@ -3045,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;
@@ -3057,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;
@@ -3108,6 +3543,20 @@ static int fg_psy_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
rc = fg_get_sram_prop(chip, FG_SRAM_VBATT_FULL, &pval->intval);
break;
+ case POWER_SUPPLY_PROP_CC_STEP:
+ if ((chip->ttf.cc_step.sel >= 0) &&
+ (chip->ttf.cc_step.sel < MAX_CC_STEPS)) {
+ pval->intval =
+ chip->ttf.cc_step.arr[chip->ttf.cc_step.sel];
+ } else {
+ pr_err("cc_step_sel is out of bounds [0, %d]\n",
+ chip->ttf.cc_step.sel);
+ return -EINVAL;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CC_STEP_SEL:
+ pval->intval = chip->ttf.cc_step.sel;
+ break;
default:
pr_err("unsupported property %d\n", psp);
rc = -EINVAL;
@@ -3140,6 +3589,74 @@ static int fg_psy_set_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
rc = fg_set_constant_chg_voltage(chip, pval->intval);
break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ rc = fg_force_esr_meas(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
+ rc = fg_prepare_for_qnovo(chip, pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_CC_STEP:
+ if ((chip->ttf.cc_step.sel >= 0) &&
+ (chip->ttf.cc_step.sel < MAX_CC_STEPS)) {
+ chip->ttf.cc_step.arr[chip->ttf.cc_step.sel] =
+ pval->intval;
+ } else {
+ pr_err("cc_step_sel is out of bounds [0, %d]\n",
+ chip->ttf.cc_step.sel);
+ return -EINVAL;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CC_STEP_SEL:
+ if ((pval->intval >= 0) && (pval->intval < MAX_CC_STEPS)) {
+ chip->ttf.cc_step.sel = pval->intval;
+ } else {
+ pr_err("cc_step_sel is out of bounds [0, %d]\n",
+ pval->intval);
+ return -EINVAL;
+ }
+ break;
+ 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;
}
@@ -3153,6 +3670,13 @@ static int fg_property_is_writeable(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ case POWER_SUPPLY_PROP_CC_STEP:
+ case POWER_SUPPLY_PROP_CC_STEP_SEL:
+ 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;
@@ -3193,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,
@@ -3213,6 +3742,8 @@ static enum power_supply_property fg_psy_props[] = {
POWER_SUPPLY_PROP_SOC_REPORTING_READY,
POWER_SUPPLY_PROP_DEBUG_BATTERY,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_CC_STEP,
+ POWER_SUPPLY_PROP_CC_STEP_SEL,
};
static const struct power_supply_desc fg_psy_desc = {
@@ -3370,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;
@@ -3423,8 +3954,7 @@ static int fg_hw_init(struct fg_chip *chip)
return rc;
}
- if (chip->cyc_ctr.en)
- restore_cycle_counter(chip);
+ restore_cycle_counter(chip);
if (chip->dt.jeita_hyst_temp >= 0) {
val = chip->dt.jeita_hyst_temp << JEITA_TEMP_HYST_SHIFT;
@@ -3651,6 +4181,11 @@ static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data)
if (rc < 0)
pr_err("Error in adjusting timebase, rc=%d\n", rc);
+ rc = fg_adjust_recharge_voltage(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting recharge_voltage, rc=%d\n",
+ rc);
+
chip->last_batt_temp = batt_temp;
power_supply_changed(chip->batt_psy);
}
@@ -3698,8 +4233,7 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data)
int rc;
fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
- if (chip->cyc_ctr.en)
- schedule_work(&chip->cycle_count_work);
+ fg_cycle_counter_update(chip);
if (chip->cl.active)
fg_cap_learning_update(chip);
@@ -4287,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);
@@ -4394,6 +4931,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
chip->charge_status = -EINVAL;
chip->prev_charge_status = -EINVAL;
chip->ki_coeff_full_soc = -EINVAL;
+ chip->online_status = -EINVAL;
chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
if (!chip->regmap) {
dev_err(chip->dev, "Parent regmap is unavailable\n");
@@ -4462,14 +5000,13 @@ static int fg_gen3_probe(struct platform_device *pdev)
mutex_init(&chip->sram_rw_lock);
mutex_init(&chip->cyc_ctr.lock);
mutex_init(&chip->cl.lock);
- mutex_init(&chip->batt_avg_lock);
+ mutex_init(&chip->ttf.lock);
mutex_init(&chip->charge_full_lock);
init_completion(&chip->soc_update);
init_completion(&chip->soc_ready);
INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work);
INIT_WORK(&chip->status_change_work, status_change_work);
- INIT_WORK(&chip->cycle_count_work, cycle_count_work);
- INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work);
+ INIT_DELAYED_WORK(&chip->ttf_work, ttf_work);
INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work);
rc = fg_memif_init(chip);
@@ -4566,7 +5103,7 @@ static int fg_gen3_suspend(struct device *dev)
if (rc < 0)
pr_err("Error in configuring ESR timer, rc=%d\n", rc);
- cancel_delayed_work_sync(&chip->batt_avg_work);
+ cancel_delayed_work_sync(&chip->ttf_work);
if (fg_sram_dump)
cancel_delayed_work_sync(&chip->sram_dump_work);
return 0;
@@ -4581,9 +5118,7 @@ static int fg_gen3_resume(struct device *dev)
if (rc < 0)
pr_err("Error in configuring ESR timer, rc=%d\n", rc);
- fg_circ_buf_clr(&chip->ibatt_circ_buf);
- fg_circ_buf_clr(&chip->vbatt_circ_buf);
- schedule_delayed_work(&chip->batt_avg_work, 0);
+ schedule_delayed_work(&chip->ttf_work, 0);
if (fg_sram_dump)
schedule_delayed_work(&chip->sram_dump_work,
msecs_to_jiffies(fg_sram_dump_period_ms));
@@ -4603,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},
{},
@@ -4617,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 eb97eb0ff2ac..53af3415ec6a 100644
--- a/drivers/power/supply/qcom/qpnp-qnovo.c
+++ b/drivers/power/supply/qcom/qpnp-qnovo.c
@@ -20,6 +20,7 @@
#include <linux/of_irq.h>
#include <linux/qpnp/qpnp-revid.h>
#include <linux/pmic-voter.h>
+#include <linux/delay.h>
#define QNOVO_REVISION1 0x00
#define QNOVO_REVISION2 0x01
@@ -111,13 +112,26 @@
#define GAIN_LSB_FACTOR 976560
#define USER_VOTER "user_voter"
+#define SHUTDOWN_VOTER "user_voter"
#define OK_TO_QNOVO_VOTER "ok_to_qnovo_voter"
#define QNOVO_VOTER "qnovo_voter"
+#define FG_AVAILABLE_VOTER "FG_AVAILABLE_VOTER"
+#define QNOVO_OVERALL_VOTER "QNOVO_OVERALL_VOTER"
+#define QNI_PT_VOTER "QNI_PT_VOTER"
+#define ESR_VOTER "ESR_VOTER"
+
+#define HW_OK_TO_QNOVO_VOTER "HW_OK_TO_QNOVO_VOTER"
+#define CHG_READY_VOTER "CHG_READY_VOTER"
+#define USB_READY_VOTER "USB_READY_VOTER"
+#define DC_READY_VOTER "DC_READY_VOTER"
+
+#define PT_RESTART_VOTER "PT_RESTART_VOTER"
struct qnovo_dt_props {
bool external_rsense;
struct device_node *revid_dev_node;
+ bool enable_for_dc;
};
struct qnovo {
@@ -127,6 +141,10 @@ struct qnovo {
struct qnovo_dt_props dt;
struct device *dev;
struct votable *disable_votable;
+ struct votable *pt_dis_votable;
+ struct votable *not_ok_to_qnovo_votable;
+ struct votable *chg_ready_votable;
+ struct votable *awake_votable;
struct class qnovo_class;
struct pmic_revid_data *pmic_rev_id;
u32 wa_flags;
@@ -138,10 +156,18 @@ struct qnovo {
s64 v_gain_mega;
struct notifier_block nb;
struct power_supply *batt_psy;
+ struct power_supply *bms_psy;
+ struct power_supply *usb_psy;
+ struct power_supply *dc_psy;
struct work_struct status_change_work;
int fv_uV_request;
int fcc_uA_request;
- bool ok_to_qnovo;
+ int usb_present;
+ int dc_present;
+ struct delayed_work usb_debounce_work;
+ struct delayed_work dc_debounce_work;
+
+ struct delayed_work ptrain_restart_work;
};
static int debug_mask;
@@ -229,6 +255,39 @@ static bool is_batt_available(struct qnovo *chip)
return true;
}
+static bool is_fg_available(struct qnovo *chip)
+{
+ if (!chip->bms_psy)
+ chip->bms_psy = power_supply_get_by_name("bms");
+
+ if (!chip->bms_psy)
+ return false;
+
+ return true;
+}
+
+static bool is_usb_available(struct qnovo *chip)
+{
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+
+ if (!chip->usb_psy)
+ return false;
+
+ return true;
+}
+
+static bool is_dc_available(struct qnovo *chip)
+{
+ if (!chip->dc_psy)
+ chip->dc_psy = power_supply_get_by_name("dc");
+
+ if (!chip->dc_psy)
+ return false;
+
+ return true;
+}
+
static int qnovo_batt_psy_update(struct qnovo *chip, bool disable)
{
union power_supply_propval pval = {0};
@@ -281,10 +340,86 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable,
return -EINVAL;
}
+ /*
+ * fg must be available for enable FG_AVAILABLE_VOTER
+ * won't enable it otherwise
+ */
+
+ if (is_fg_available(chip))
+ power_supply_set_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
+ &pval);
+
+ vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, disable, 0);
rc = qnovo_batt_psy_update(chip, disable);
return rc;
}
+static int pt_dis_votable_cb(struct votable *votable, void *data, int disable,
+ const char *client)
+{
+ struct qnovo *chip = data;
+ int rc;
+
+ if (disable) {
+ cancel_delayed_work_sync(&chip->ptrain_restart_work);
+ vote(chip->awake_votable, PT_RESTART_VOTER, false, 0);
+ }
+
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
+ (bool)disable ? 0 : QNOVO_PTRAIN_EN_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
+ (bool)disable ? "disable" : "enable", rc);
+ return rc;
+ }
+
+ if (!disable) {
+ vote(chip->awake_votable, PT_RESTART_VOTER, true, 0);
+ schedule_delayed_work(&chip->ptrain_restart_work,
+ msecs_to_jiffies(20));
+ }
+
+ return 0;
+}
+
+static int not_ok_to_qnovo_cb(struct votable *votable, void *data,
+ int not_ok_to_qnovo,
+ const char *client)
+{
+ struct qnovo *chip = data;
+
+ vote(chip->disable_votable, OK_TO_QNOVO_VOTER, not_ok_to_qnovo, 0);
+ if (not_ok_to_qnovo)
+ vote(chip->disable_votable, USER_VOTER, true, 0);
+
+ kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
+ return 0;
+}
+
+static int chg_ready_cb(struct votable *votable, void *data, int ready,
+ const char *client)
+{
+ struct qnovo *chip = data;
+
+ vote(chip->not_ok_to_qnovo_votable, CHG_READY_VOTER, !ready, 0);
+
+ return 0;
+}
+
+static int awake_cb(struct votable *votable, void *data, int awake,
+ const char *client)
+{
+ struct qnovo *chip = data;
+
+ if (awake)
+ pm_stay_awake(chip->dev);
+ else
+ pm_relax(chip->dev);
+
+ return 0;
+}
+
static int qnovo_parse_dt(struct qnovo *chip)
{
struct device_node *node = chip->dev->of_node;
@@ -309,6 +444,8 @@ static int qnovo_parse_dt(struct qnovo *chip)
pr_err("Missing qcom,pmic-revid property - driver failed\n");
return -EINVAL;
}
+ chip->dt.enable_for_dc = of_property_read_bool(node,
+ "qcom,enable-for-dc");
return 0;
}
@@ -626,8 +763,9 @@ static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr,
char *buf)
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ int val = get_effective_result(chip->not_ok_to_qnovo_votable);
- return snprintf(buf, PAGE_SIZE, "%d\n", chip->ok_to_qnovo);
+ return snprintf(buf, PAGE_SIZE, "%d\n", !val);
}
static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr,
@@ -656,21 +794,10 @@ static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr,
static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr,
char *ubuf)
{
- int i = attr - qnovo_attributes;
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
- u8 buf[2] = {0, 0};
- u16 regval;
- int rc;
+ int val = get_effective_result(chip->pt_dis_votable);
- rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
- if (rc < 0) {
- pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
- return -EINVAL;
- }
- regval = buf[1] << 8 | buf[0];
-
- return snprintf(ubuf, PAGE_SIZE, "%d\n",
- (int)(regval & QNOVO_PTRAIN_EN_BIT));
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", !val);
}
static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr,
@@ -678,21 +805,12 @@ static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr,
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
unsigned long val;
- int rc = 0;
-
- if (get_effective_result(chip->disable_votable))
- return -EINVAL;
if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
- rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
- (bool)val ? QNOVO_PTRAIN_EN_BIT : 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
- (bool)val ? "enable" : "disable", rc);
- return rc;
- }
+ /* val being 0, userspace wishes to disable pt so vote true */
+ vote(chip->pt_dis_votable, QNI_PT_VOTER, val ? false : true, 0);
return count;
}
@@ -1116,41 +1234,170 @@ static int qnovo_update_status(struct qnovo *chip)
{
u8 val = 0;
int rc;
- bool ok_to_qnovo;
- bool changed = false;
+ bool hw_ok_to_qnovo;
rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1);
if (rc < 0) {
pr_err("Couldn't read error sts rc = %d\n", rc);
- ok_to_qnovo = false;
+ hw_ok_to_qnovo = false;
} else {
/*
* For CV mode keep qnovo enabled, userspace is expected to
* disable it after few runs
*/
- ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ? true : false;
+ hw_ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ?
+ true : false;
}
- if (chip->ok_to_qnovo ^ ok_to_qnovo) {
+ vote(chip->not_ok_to_qnovo_votable, HW_OK_TO_QNOVO_VOTER,
+ !hw_ok_to_qnovo, 0);
+ return 0;
+}
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !ok_to_qnovo, 0);
- if (!ok_to_qnovo)
- vote(chip->disable_votable, USER_VOTER, true, 0);
+static void usb_debounce_work(struct work_struct *work)
+{
+ struct qnovo *chip = container_of(work,
+ struct qnovo, usb_debounce_work.work);
- chip->ok_to_qnovo = ok_to_qnovo;
- changed = true;
- }
+ vote(chip->chg_ready_votable, USB_READY_VOTER, true, 0);
+ vote(chip->awake_votable, USB_READY_VOTER, false, 0);
+}
- return changed;
+static void dc_debounce_work(struct work_struct *work)
+{
+ struct qnovo *chip = container_of(work,
+ struct qnovo, dc_debounce_work.work);
+
+ vote(chip->chg_ready_votable, DC_READY_VOTER, true, 0);
+ vote(chip->awake_votable, DC_READY_VOTER, false, 0);
}
+#define DEBOUNCE_MS 15000 /* 15 seconds */
static void status_change_work(struct work_struct *work)
{
struct qnovo *chip = container_of(work,
struct qnovo, status_change_work);
+ union power_supply_propval pval;
+ bool usb_present = false, dc_present = false;
+ int rc;
+
+ if (is_fg_available(chip))
+ vote(chip->disable_votable, FG_AVAILABLE_VOTER, false, 0);
+
+ if (is_usb_available(chip)) {
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ usb_present = (rc < 0) ? 0 : pval.intval;
+ }
+
+ if (chip->usb_present && !usb_present) {
+ /* removal */
+ chip->usb_present = 0;
+ cancel_delayed_work_sync(&chip->usb_debounce_work);
+ vote(chip->awake_votable, USB_READY_VOTER, false, 0);
+ vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0);
+ } else if (!chip->usb_present && usb_present) {
+ /* insertion */
+ chip->usb_present = 1;
+ vote(chip->awake_votable, USB_READY_VOTER, true, 0);
+ schedule_delayed_work(&chip->usb_debounce_work,
+ msecs_to_jiffies(DEBOUNCE_MS));
+ }
+
+ if (is_dc_available(chip)) {
+ rc = power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_PRESENT,
+ &pval);
+ dc_present = (rc < 0) ? 0 : pval.intval;
+ }
- if (qnovo_update_status(chip))
- kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
+ if (usb_present)
+ dc_present = 0;
+
+ /* disable qnovo for dc path by forcing dc_present = 0 always */
+ if (!chip->dt.enable_for_dc)
+ dc_present = 0;
+
+ if (chip->dc_present && !dc_present) {
+ /* removal */
+ chip->dc_present = 0;
+ cancel_delayed_work_sync(&chip->dc_debounce_work);
+ vote(chip->awake_votable, DC_READY_VOTER, false, 0);
+ vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0);
+ } else if (!chip->dc_present && dc_present) {
+ /* insertion */
+ chip->dc_present = 1;
+ vote(chip->awake_votable, DC_READY_VOTER, true, 0);
+ schedule_delayed_work(&chip->dc_debounce_work,
+ msecs_to_jiffies(DEBOUNCE_MS));
+ }
+
+ qnovo_update_status(chip);
+}
+
+static void ptrain_restart_work(struct work_struct *work)
+{
+ struct qnovo *chip = container_of(work,
+ struct qnovo, ptrain_restart_work.work);
+ u8 pt_t1, pt_t2;
+ int rc;
+ u8 pt_en;
+
+ rc = qnovo_read(chip, QNOVO_PTRAIN_EN, &pt_en, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read QNOVO_PTRAIN_EN rc = %d\n",
+ rc);
+ goto clean_up;
+ }
+
+ if (!pt_en) {
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN,
+ QNOVO_PTRAIN_EN_BIT, QNOVO_PTRAIN_EN_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't enable pulse train rc=%d\n",
+ rc);
+ goto clean_up;
+ }
+ /* sleep 20ms for the pulse trains to restart and settle */
+ msleep(20);
+ }
+
+ rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t1, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n",
+ rc);
+ goto clean_up;
+ }
+
+ /* pttime increments every 2 seconds */
+ msleep(2100);
+
+ rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t2, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n",
+ rc);
+ goto clean_up;
+ }
+
+ if (pt_t1 != pt_t2)
+ goto clean_up;
+
+ /* Toggle pt enable to restart pulse train */
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't disable pulse train rc=%d\n", rc);
+ goto clean_up;
+ }
+ msleep(1000);
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
+ QNOVO_PTRAIN_EN_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't enable pulse train rc=%d\n", rc);
+ goto clean_up;
+ }
+
+clean_up:
+ vote(chip->awake_votable, PT_RESTART_VOTER, false, 0);
}
static int qnovo_notifier_call(struct notifier_block *nb,
@@ -1162,7 +1409,10 @@ static int qnovo_notifier_call(struct notifier_block *nb,
if (ev != PSY_EVENT_PROP_CHANGED)
return NOTIFY_OK;
- if (strcmp(psy->desc->name, "battery") == 0)
+ if (strcmp(psy->desc->name, "battery") == 0
+ || strcmp(psy->desc->name, "bms") == 0
+ || strcmp(psy->desc->name, "usb") == 0
+ || strcmp(psy->desc->name, "dc") == 0)
schedule_work(&chip->status_change_work);
return NOTIFY_OK;
@@ -1171,8 +1421,27 @@ static int qnovo_notifier_call(struct notifier_block *nb,
static irqreturn_t handle_ptrain_done(int irq, void *data)
{
struct qnovo *chip = data;
+ union power_supply_propval pval = {0};
qnovo_update_status(chip);
+
+ /*
+ * hw resets pt_en bit once ptrain_done triggers.
+ * vote on behalf of QNI to disable it such that
+ * once QNI enables it, the votable state changes
+ * and the callback that sets it is indeed invoked
+ */
+ vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0);
+
+ vote(chip->pt_dis_votable, ESR_VOTER, true, 0);
+ if (is_fg_available(chip)
+ && !get_client_vote(chip->disable_votable, USER_VOTER)
+ && !get_effective_result(chip->not_ok_to_qnovo_votable))
+ power_supply_set_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ &pval);
+
+ vote(chip->pt_dis_votable, ESR_VOTER, false, 0);
kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
return IRQ_HANDLED;
}
@@ -1185,7 +1454,15 @@ static int qnovo_hw_init(struct qnovo *chip)
u8 vadc_offset, vadc_gain;
u8 val;
+ vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0);
+ vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0);
+
vote(chip->disable_votable, USER_VOTER, true, 0);
+ vote(chip->disable_votable, FG_AVAILABLE_VOTER, true, 0);
+
+ vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0);
+ vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, true, 0);
+ vote(chip->pt_dis_votable, ESR_VOTER, false, 0);
val = 0;
rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
@@ -1349,12 +1626,45 @@ static int qnovo_probe(struct platform_device *pdev)
goto cleanup;
}
+ chip->pt_dis_votable = create_votable("QNOVO_PT_DIS", VOTE_SET_ANY,
+ pt_dis_votable_cb, chip);
+ if (IS_ERR(chip->pt_dis_votable)) {
+ rc = PTR_ERR(chip->pt_dis_votable);
+ goto destroy_disable_votable;
+ }
+
+ chip->not_ok_to_qnovo_votable = create_votable("QNOVO_NOT_OK",
+ VOTE_SET_ANY,
+ not_ok_to_qnovo_cb, chip);
+ if (IS_ERR(chip->not_ok_to_qnovo_votable)) {
+ rc = PTR_ERR(chip->not_ok_to_qnovo_votable);
+ goto destroy_pt_dis_votable;
+ }
+
+ chip->chg_ready_votable = create_votable("QNOVO_CHG_READY",
+ VOTE_SET_ANY,
+ chg_ready_cb, chip);
+ if (IS_ERR(chip->chg_ready_votable)) {
+ rc = PTR_ERR(chip->chg_ready_votable);
+ goto destroy_not_ok_to_qnovo_votable;
+ }
+
+ chip->awake_votable = create_votable("QNOVO_AWAKE", VOTE_SET_ANY,
+ awake_cb, chip);
+ if (IS_ERR(chip->awake_votable)) {
+ rc = PTR_ERR(chip->awake_votable);
+ goto destroy_chg_ready_votable;
+ }
+
INIT_WORK(&chip->status_change_work, status_change_work);
+ INIT_DELAYED_WORK(&chip->dc_debounce_work, dc_debounce_work);
+ INIT_DELAYED_WORK(&chip->usb_debounce_work, usb_debounce_work);
+ INIT_DELAYED_WORK(&chip->ptrain_restart_work, ptrain_restart_work);
rc = qnovo_hw_init(chip);
if (rc < 0) {
pr_err("Couldn't initialize hardware rc=%d\n", rc);
- goto destroy_votable;
+ goto destroy_awake_votable;
}
rc = qnovo_register_notifier(chip);
@@ -1390,7 +1700,15 @@ static int qnovo_probe(struct platform_device *pdev)
unreg_notifier:
power_supply_unreg_notifier(&chip->nb);
-destroy_votable:
+destroy_awake_votable:
+ destroy_votable(chip->awake_votable);
+destroy_chg_ready_votable:
+ destroy_votable(chip->chg_ready_votable);
+destroy_not_ok_to_qnovo_votable:
+ destroy_votable(chip->not_ok_to_qnovo_votable);
+destroy_pt_dis_votable:
+ destroy_votable(chip->pt_dis_votable);
+destroy_disable_votable:
destroy_votable(chip->disable_votable);
cleanup:
platform_set_drvdata(pdev, NULL);
@@ -1403,11 +1721,21 @@ static int qnovo_remove(struct platform_device *pdev)
class_unregister(&chip->qnovo_class);
power_supply_unreg_notifier(&chip->nb);
+ destroy_votable(chip->chg_ready_votable);
+ destroy_votable(chip->not_ok_to_qnovo_votable);
+ destroy_votable(chip->pt_dis_votable);
destroy_votable(chip->disable_votable);
platform_set_drvdata(pdev, NULL);
return 0;
}
+static void qnovo_shutdown(struct platform_device *pdev)
+{
+ struct qnovo *chip = platform_get_drvdata(pdev);
+
+ vote(chip->not_ok_to_qnovo_votable, SHUTDOWN_VOTER, true, 0);
+}
+
static const struct of_device_id match_table[] = {
{ .compatible = "qcom,qpnp-qnovo", },
{ },
@@ -1421,6 +1749,7 @@ static struct platform_driver qnovo_driver = {
},
.probe = qnovo_probe,
.remove = qnovo_remove,
+ .shutdown = qnovo_shutdown,
};
module_platform_driver(qnovo_driver);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 7de9429cae8b..ea205100644d 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -210,6 +210,9 @@ static int smb2_parse_dt(struct smb2 *chip)
chg->step_chg_enabled = of_property_read_bool(node,
"qcom,step-charging-enable");
+ chg->sw_jeita_enabled = of_property_read_bool(node,
+ "qcom,sw-jeita-enable");
+
rc = of_property_read_u32(node, "qcom,wd-bark-time-secs",
&chip->dt.wd_bark_time);
if (rc < 0 || chip->dt.wd_bark_time < MIN_WD_BARK_TIME)
@@ -327,7 +330,6 @@ static int smb2_parse_dt(struct smb2 *chip)
static enum power_supply_property smb2_usb_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_PD_CURRENT_MAX,
@@ -346,6 +348,9 @@ static enum power_supply_property smb2_usb_props[] = {
POWER_SUPPLY_PROP_HW_CURRENT_MAX,
POWER_SUPPLY_PROP_REAL_TYPE,
POWER_SUPPLY_PROP_PR_SWAP,
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
};
static int smb2_usb_get_prop(struct power_supply *psy,
@@ -377,20 +382,17 @@ static int smb2_usb_get_prop(struct power_supply *psy,
if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN)
val->intval = 0;
break;
- case POWER_SUPPLY_PROP_VOLTAGE_MIN:
- val->intval = chg->voltage_min_uv;
- break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- val->intval = chg->voltage_max_uv;
+ rc = smblib_get_prop_usb_voltage_max(chg, val);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
rc = smblib_get_prop_usb_voltage_now(chg, val);
break;
case POWER_SUPPLY_PROP_PD_CURRENT_MAX:
- rc = smblib_get_prop_pd_current_max(chg, val);
+ val->intval = get_client_vote(chg->usb_icl_votable, PD_VOTER);
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
- rc = smblib_get_prop_usb_current_max(chg, val);
+ rc = smblib_get_prop_input_current_settled(chg, val);
break;
case POWER_SUPPLY_PROP_TYPE:
val->intval = POWER_SUPPLY_TYPE_USB_PD;
@@ -454,6 +456,16 @@ static int smb2_usb_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_PR_SWAP:
rc = smblib_get_prop_pr_swap_in_progress(chg, val);
break;
+ case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX:
+ val->intval = chg->voltage_max_uv;
+ break;
+ case POWER_SUPPLY_PROP_PD_VOLTAGE_MIN:
+ val->intval = chg->voltage_min_uv;
+ break;
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ val->intval = get_client_vote(chg->usb_icl_votable,
+ USB_PSY_VOTER);
+ break;
default:
pr_err("get prop %d is not supported in usb\n", psp);
rc = -EINVAL;
@@ -481,18 +493,9 @@ static int smb2_usb_set_prop(struct power_supply *psy,
}
switch (psp) {
- case POWER_SUPPLY_PROP_VOLTAGE_MIN:
- rc = smblib_set_prop_usb_voltage_min(chg, val);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- rc = smblib_set_prop_usb_voltage_max(chg, val);
- break;
case POWER_SUPPLY_PROP_PD_CURRENT_MAX:
rc = smblib_set_prop_pd_current_max(chg, val);
break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- rc = smblib_set_prop_usb_current_max(chg, val);
- break;
case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE:
rc = smblib_set_prop_typec_power_role(chg, val);
break;
@@ -515,6 +518,15 @@ static int smb2_usb_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_PR_SWAP:
rc = smblib_set_prop_pr_swap_in_progress(chg, val);
break;
+ case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX:
+ rc = smblib_set_prop_pd_voltage_max(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_PD_VOLTAGE_MIN:
+ rc = smblib_set_prop_pd_voltage_min(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ rc = smblib_set_prop_sdp_current_max(chg, val);
+ break;
default:
pr_err("set prop %d is not supported\n", psp);
rc = -EINVAL;
@@ -530,8 +542,6 @@ static int smb2_usb_prop_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE:
case POWER_SUPPLY_PROP_CTM_CURRENT_MAX:
return 1;
default:
@@ -573,6 +583,8 @@ static int smb2_init_usb_psy(struct smb2 *chip)
static enum power_supply_property smb2_usb_port_props[] = {
POWER_SUPPLY_PROP_TYPE,
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
};
static int smb2_usb_port_get_prop(struct power_supply *psy,
@@ -599,6 +611,12 @@ static int smb2_usb_port_get_prop(struct power_supply *psy,
else
val->intval = 0;
break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ val->intval = 5000000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ rc = smblib_get_prop_input_current_settled(chg, val);
+ break;
default:
pr_err_ratelimited("Get prop %d is not supported in pc_port\n",
psp);
@@ -777,9 +795,11 @@ 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,
+ POWER_SUPPLY_PROP_REAL_TYPE,
};
static int smb2_dc_get_prop(struct power_supply *psy,
@@ -791,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;
@@ -800,6 +823,9 @@ static int smb2_dc_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CURRENT_MAX:
rc = smblib_get_prop_dc_current_max(chg, val);
break;
+ case POWER_SUPPLY_PROP_REAL_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_WIPOWER;
+ break;
default:
return -EINVAL;
}
@@ -819,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;
@@ -848,7 +878,7 @@ static int smb2_dc_prop_is_writeable(struct power_supply *psy,
static const struct power_supply_desc dc_psy_desc = {
.name = "dc",
- .type = POWER_SUPPLY_TYPE_WIPOWER,
+ .type = POWER_SUPPLY_TYPE_WIRELESS,
.properties = smb2_dc_props,
.num_properties = ARRAY_SIZE(smb2_dc_props),
.get_property = smb2_dc_get_prop,
@@ -898,12 +928,14 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_SW_JEITA_ENABLED,
POWER_SUPPLY_PROP_CHARGE_DONE,
POWER_SUPPLY_PROP_PARALLEL_DISABLE,
POWER_SUPPLY_PROP_SET_SHIP_MODE,
POWER_SUPPLY_PROP_DIE_HEALTH,
POWER_SUPPLY_PROP_RERUN_AICL,
POWER_SUPPLY_PROP_DP_DM,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
};
static int smb2_batt_get_prop(struct power_supply *psy,
@@ -955,6 +987,9 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
val->intval = chg->step_chg_enabled;
break;
+ case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
+ val->intval = chg->sw_jeita_enabled;
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
rc = smblib_get_prop_batt_voltage_now(chg, val);
break;
@@ -1006,6 +1041,9 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_RERUN_AICL:
val->intval = 0;
break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ rc = smblib_get_prop_batt_charge_counter(chg, val);
+ break;
default:
pr_err("batt power supply prop %d not supported\n", psp);
return -EINVAL;
@@ -1071,6 +1109,13 @@ static int smb2_batt_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
chg->step_chg_enabled = !!val->intval;
break;
+ case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
+ if (chg->sw_jeita_enabled != (!!val->intval)) {
+ rc = smblib_disable_hw_jeita(chg, !!val->intval);
+ if (rc == 0)
+ chg->sw_jeita_enabled = !!val->intval;
+ }
+ break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
chg->batt_profile_fcc_ua = val->intval;
vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, val->intval);
@@ -1112,6 +1157,7 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy,
case POWER_SUPPLY_PROP_RERUN_AICL:
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
return 1;
default:
break;
@@ -1153,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,
@@ -1195,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,
@@ -1641,6 +1687,13 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+ rc = smblib_read(chg, USBIN_OPTIONS_2_CFG_REG, &chg->float_cfg);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read float charger options rc=%d\n",
+ rc);
+ return rc;
+ }
+
switch (chip->dt.chg_inhibit_thr_mv) {
case 50:
rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG,
@@ -1697,6 +1750,14 @@ static int smb2_init_hw(struct smb2 *chip)
}
}
+ if (chg->sw_jeita_enabled) {
+ rc = smblib_disable_hw_jeita(chg, true);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't set hw jeita rc=%d\n", rc);
+ return rc;
+ }
+ }
+
return rc;
}
@@ -1769,8 +1830,8 @@ static int smb2_chg_config_init(struct smb2 *chip)
chip->chg.wa_flags |= BOOST_BACK_WA | OTG_WA;
chg->param.freq_buck = pm660_params.freq_buck;
chg->param.freq_boost = pm660_params.freq_boost;
- chg->chg_freq.freq_5V = 600;
- chg->chg_freq.freq_6V_8V = 800;
+ chg->chg_freq.freq_5V = 650;
+ chg->chg_freq.freq_6V_8V = 850;
chg->chg_freq.freq_9V = 1050;
chg->chg_freq.freq_12V = 1200;
chg->chg_freq.freq_removal = 1050;
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 52775a3bdf1b..f9d35ea7775b 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -420,19 +420,21 @@ static int smblib_set_adapter_allowance(struct smb_charger *chg,
{
int rc = 0;
- switch (allowed_voltage) {
- case USBIN_ADAPTER_ALLOW_12V:
- case USBIN_ADAPTER_ALLOW_5V_OR_12V:
- case USBIN_ADAPTER_ALLOW_9V_TO_12V:
- case USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V:
- case USBIN_ADAPTER_ALLOW_5V_TO_12V:
- /* PM660 only support max. 9V */
- if (chg->smb_version == PM660_SUBTYPE) {
- smblib_dbg(chg, PR_MISC, "voltage not supported=%d\n",
- allowed_voltage);
+ /* PM660 only support max. 9V */
+ if (chg->smb_version == PM660_SUBTYPE) {
+ switch (allowed_voltage) {
+ case USBIN_ADAPTER_ALLOW_12V:
+ case USBIN_ADAPTER_ALLOW_9V_TO_12V:
+ allowed_voltage = USBIN_ADAPTER_ALLOW_9V;
+ break;
+ case USBIN_ADAPTER_ALLOW_5V_OR_12V:
+ case USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V:
allowed_voltage = USBIN_ADAPTER_ALLOW_5V_OR_9V;
+ break;
+ case USBIN_ADAPTER_ALLOW_5V_TO_12V:
+ allowed_voltage = USBIN_ADAPTER_ALLOW_5V_TO_9V;
+ break;
}
- break;
}
rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage);
@@ -488,6 +490,45 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg,
/********************
* HELPER FUNCTIONS *
********************/
+static int smblib_request_dpdm(struct smb_charger *chg, bool enable)
+{
+ int rc = 0;
+
+ /* fetch the DPDM regulator */
+ if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
+ "dpdm-supply", NULL)) {
+ chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm");
+ if (IS_ERR(chg->dpdm_reg)) {
+ rc = PTR_ERR(chg->dpdm_reg);
+ smblib_err(chg, "Couldn't get dpdm regulator rc=%d\n",
+ rc);
+ chg->dpdm_reg = NULL;
+ return rc;
+ }
+ }
+
+ if (enable) {
+ if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n");
+ rc = regulator_enable(chg->dpdm_reg);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't enable dpdm regulator rc=%d\n",
+ rc);
+ }
+ } else {
+ if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
+ rc = regulator_disable(chg->dpdm_reg);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't disable dpdm regulator rc=%d\n",
+ rc);
+ }
+ }
+
+ return rc;
+}
static void smblib_rerun_apsd(struct smb_charger *chg)
{
@@ -514,10 +555,17 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
/* if PD is active, APSD is disabled so won't have a valid result */
- if (chg->pd_active)
+ if (chg->pd_active) {
chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD;
- else
+ } else {
+ /*
+ * Update real charger type only if its not FLOAT
+ * detected as as SDP
+ */
+ if (!(apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT &&
+ chg->real_charger_type == POWER_SUPPLY_TYPE_USB))
chg->real_charger_type = apsd_result->pst;
+ }
smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n",
apsd_result->name, chg->pd_active);
@@ -600,13 +648,9 @@ static void smblib_uusb_removal(struct smb_charger *chg)
cancel_delayed_work_sync(&chg->pl_enable_work);
- if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
- smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
- rc = regulator_disable(chg->dpdm_reg);
- if (rc < 0)
- smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
- rc);
- }
+ rc = smblib_request_dpdm(chg, false);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't to disable DPDM rc=%d\n", rc);
if (chg->wa_flags & BOOST_BACK_WA) {
data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
@@ -698,24 +742,9 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg)
if (!val.intval)
return 0;
- /* fetch the DPDM regulator */
- if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
- "dpdm-supply", NULL)) {
- chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm");
- if (IS_ERR(chg->dpdm_reg)) {
- smblib_err(chg, "Couldn't get dpdm regulator rc=%ld\n",
- PTR_ERR(chg->dpdm_reg));
- chg->dpdm_reg = NULL;
- }
- }
-
- if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) {
- smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n");
- rc = regulator_enable(chg->dpdm_reg);
- if (rc < 0)
- smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n",
- rc);
- }
+ rc = smblib_request_dpdm(chg, true);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
chg->uusb_apsd_rerun_done = true;
smblib_rerun_apsd(chg);
@@ -785,6 +814,7 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua)
{
int rc;
u8 icl_options;
+ const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
/* power source is SDP */
switch (icl_ua) {
@@ -809,6 +839,21 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua)
return -EINVAL;
}
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB &&
+ apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT) {
+ /*
+ * change the float charger configuration to SDP, if this
+ * is the case of SDP being detected as FLOAT
+ */
+ rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
+ FORCE_FLOAT_SDP_CFG_BIT, FORCE_FLOAT_SDP_CFG_BIT);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't set float ICL options rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
CFG_USB3P0_SEL_BIT | USB51_MODE_BIT, icl_options);
if (rc < 0) {
@@ -1528,8 +1573,8 @@ int smblib_get_prop_batt_status(struct smb_charger *chg,
union power_supply_propval *val)
{
union power_supply_propval pval = {0, };
- bool usb_online, dc_online;
- u8 stat;
+ bool usb_online, dc_online, qnovo_en;
+ u8 stat, pt_en_cmd;
int rc;
rc = smblib_get_prop_usb_online(chg, &pval);
@@ -1597,11 +1642,22 @@ int smblib_get_prop_batt_status(struct smb_charger *chg,
smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
rc);
return rc;
- }
+ }
stat &= ENABLE_TRICKLE_BIT | ENABLE_PRE_CHARGING_BIT |
ENABLE_FAST_CHARGING_BIT | ENABLE_FULLON_MODE_BIT;
- if (!stat)
+
+ rc = smblib_read(chg, QNOVO_PT_ENABLE_CMD_REG, &pt_en_cmd);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read QNOVO_PT_ENABLE_CMD_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ qnovo_en = (bool)(pt_en_cmd & QNOVO_PT_ENABLE_CMD_BIT);
+
+ /* ignore stat7 when qnovo is enabled */
+ if (!qnovo_en && !stat)
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
return 0;
@@ -1789,6 +1845,19 @@ int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg,
return 0;
}
+int smblib_get_prop_batt_charge_counter(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+
+ if (!chg->bms_psy)
+ return -EINVAL;
+
+ rc = power_supply_get_property(chg->bms_psy,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER, val);
+ return rc;
+}
+
/***********************
* BATTERY PSY SETTERS *
***********************/
@@ -1999,6 +2068,29 @@ int smblib_dp_dm(struct smb_charger *chg, int val)
return rc;
}
+int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable)
+{
+ int rc;
+ u8 mask;
+
+ /*
+ * Disable h/w base JEITA compensation if s/w JEITA is enabled
+ */
+ mask = JEITA_EN_COLD_SL_FCV_BIT
+ | JEITA_EN_HOT_SL_FCV_BIT
+ | JEITA_EN_HOT_SL_CCC_BIT
+ | JEITA_EN_COLD_SL_CCC_BIT,
+ rc = smblib_masked_write(chg, JEITA_EN_CFG_REG, mask,
+ disable ? 0 : mask);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't configure s/w jeita rc=%d\n",
+ rc);
+ return rc;
+ }
+ return 0;
+}
+
/*******************
* DC PSY GETTERS *
*******************/
@@ -2110,6 +2202,25 @@ int smblib_get_prop_usb_online(struct smb_charger *chg,
return rc;
}
+int smblib_get_prop_usb_voltage_max(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ switch (chg->real_charger_type) {
+ case POWER_SUPPLY_TYPE_USB_HVDCP:
+ case POWER_SUPPLY_TYPE_USB_PD:
+ if (chg->smb_version == PM660_SUBTYPE)
+ val->intval = MICRO_9V;
+ else
+ val->intval = MICRO_12V;
+ break;
+ default:
+ val->intval = MICRO_5V;
+ break;
+ }
+
+ return 0;
+}
+
int smblib_get_prop_usb_voltage_now(struct smb_charger *chg,
union power_supply_propval *val)
{
@@ -2129,21 +2240,6 @@ int smblib_get_prop_usb_voltage_now(struct smb_charger *chg,
return iio_read_channel_processed(chg->iio.usbin_v_chan, &val->intval);
}
-int smblib_get_prop_pd_current_max(struct smb_charger *chg,
- union power_supply_propval *val)
-{
- val->intval = get_client_vote_locked(chg->usb_icl_votable, PD_VOTER);
- return 0;
-}
-
-int smblib_get_prop_usb_current_max(struct smb_charger *chg,
- union power_supply_propval *val)
-{
- val->intval = get_client_vote_locked(chg->usb_icl_votable,
- USB_PSY_VOTER);
- return 0;
-}
-
int smblib_get_prop_usb_current_now(struct smb_charger *chg,
union power_supply_propval *val)
{
@@ -2321,16 +2417,9 @@ int smblib_get_prop_input_current_settled(struct smb_charger *chg,
int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
union power_supply_propval *val)
{
- const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
int rc, pulses;
- val->intval = MICRO_5V;
- if (apsd_result == NULL) {
- smblib_err(chg, "APSD result is NULL\n");
- return 0;
- }
-
- switch (apsd_result->pst) {
+ switch (chg->real_charger_type) {
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
rc = smblib_get_pulse_cnt(chg, &pulses);
if (rc < 0) {
@@ -2340,6 +2429,9 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
}
val->intval = MICRO_5V + HVDCP3_STEP_UV * pulses;
break;
+ case POWER_SUPPLY_TYPE_USB_PD:
+ val->intval = chg->voltage_min_uv;
+ break;
default:
val->intval = MICRO_5V;
break;
@@ -2402,6 +2494,31 @@ int smblib_get_prop_die_health(struct smb_charger *chg,
return 0;
}
+#define SDP_CURRENT_UA 500000
+#define CDP_CURRENT_UA 1500000
+#define DCP_CURRENT_UA 1500000
+#define HVDCP_CURRENT_UA 3000000
+#define TYPEC_DEFAULT_CURRENT_UA 900000
+#define TYPEC_MEDIUM_CURRENT_UA 1500000
+#define TYPEC_HIGH_CURRENT_UA 3000000
+static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode)
+{
+ int rp_ua;
+
+ switch (typec_mode) {
+ case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+ rp_ua = TYPEC_HIGH_CURRENT_UA;
+ break;
+ case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+ case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+ /* fall through */
+ default:
+ rp_ua = DCP_CURRENT_UA;
+ }
+
+ return rp_ua;
+}
+
/*******************
* USB PSY SETTERS *
* *****************/
@@ -2419,14 +2536,54 @@ int smblib_set_prop_pd_current_max(struct smb_charger *chg,
return rc;
}
-int smblib_set_prop_usb_current_max(struct smb_charger *chg,
+static int smblib_handle_usb_current(struct smb_charger *chg,
+ int usb_current)
+{
+ int rc = 0, rp_ua, typec_mode;
+
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
+ if (usb_current == -ETIMEDOUT) {
+ /*
+ * Valid FLOAT charger, report the current based
+ * of Rp
+ */
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ rp_ua = get_rp_based_dcp_current(chg, typec_mode);
+ rc = vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER,
+ true, rp_ua);
+ if (rc < 0)
+ return rc;
+ } else {
+ /*
+ * FLOAT charger detected as SDP by USB driver,
+ * charge with the requested current and update the
+ * real_charger_type
+ */
+ chg->real_charger_type = POWER_SUPPLY_TYPE_USB;
+ rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
+ true, usb_current);
+ if (rc < 0)
+ return rc;
+ rc = vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER,
+ false, 0);
+ if (rc < 0)
+ return rc;
+ }
+ } else {
+ rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
+ true, usb_current);
+ }
+
+ return rc;
+}
+
+int smblib_set_prop_sdp_current_max(struct smb_charger *chg,
const union power_supply_propval *val)
{
int rc = 0;
if (!chg->pd_active) {
- rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
- true, val->intval);
+ rc = smblib_handle_usb_current(chg, val->intval);
} else if (chg->system_suspend_supported) {
if (val->intval <= USBIN_25MA)
rc = vote(chg->usb_icl_votable,
@@ -2507,7 +2664,7 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg,
return rc;
}
-int smblib_set_prop_usb_voltage_min(struct smb_charger *chg,
+int smblib_set_prop_pd_voltage_min(struct smb_charger *chg,
const union power_supply_propval *val)
{
int rc, min_uv;
@@ -2522,10 +2679,11 @@ int smblib_set_prop_usb_voltage_min(struct smb_charger *chg,
}
chg->voltage_min_uv = min_uv;
+ power_supply_changed(chg->usb_main_psy);
return rc;
}
-int smblib_set_prop_usb_voltage_max(struct smb_charger *chg,
+int smblib_set_prop_pd_voltage_max(struct smb_charger *chg,
const union power_supply_propval *val)
{
int rc, max_uv;
@@ -2875,14 +3033,6 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg,
/***********************
* USB MAIN PSY SETTERS *
*************************/
-
-#define SDP_CURRENT_UA 500000
-#define CDP_CURRENT_UA 1500000
-#define DCP_CURRENT_UA 1500000
-#define HVDCP_CURRENT_UA 3000000
-#define TYPEC_DEFAULT_CURRENT_UA 900000
-#define TYPEC_MEDIUM_CURRENT_UA 1500000
-#define TYPEC_HIGH_CURRENT_UA 3000000
int smblib_get_charge_current(struct smb_charger *chg,
int *total_current_ua)
{
@@ -3173,25 +3323,10 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
smblib_set_opt_freq_buck(chg, vbus_rising ? chg->chg_freq.freq_5V :
chg->chg_freq.freq_removal);
- /* fetch the DPDM regulator */
- if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
- "dpdm-supply", NULL)) {
- chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm");
- if (IS_ERR(chg->dpdm_reg)) {
- smblib_err(chg, "Couldn't get dpdm regulator rc=%ld\n",
- PTR_ERR(chg->dpdm_reg));
- chg->dpdm_reg = NULL;
- }
- }
-
if (vbus_rising) {
- if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) {
- smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n");
- rc = regulator_enable(chg->dpdm_reg);
- if (rc < 0)
- smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n",
- rc);
- }
+ rc = smblib_request_dpdm(chg, true);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
/* Schedule work to enable parallel charger */
vote(chg->awake_votable, PL_DELAY_VOTER, true, 0);
@@ -3211,13 +3346,9 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
}
}
- if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
- smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
- rc = regulator_disable(chg->dpdm_reg);
- if (rc < 0)
- smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
- rc);
- }
+ rc = smblib_request_dpdm(chg, false);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
}
if (chg->micro_usb_mode)
@@ -3438,24 +3569,6 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
rising ? "rising" : "falling");
}
-static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode)
-{
- int rp_ua;
-
- switch (typec_mode) {
- case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- rp_ua = TYPEC_HIGH_CURRENT_UA;
- break;
- case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
- /* fall through */
- default:
- rp_ua = DCP_CURRENT_UA;
- }
-
- return rp_ua;
-}
-
static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
{
int typec_mode;
@@ -3481,11 +3594,17 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
break;
case POWER_SUPPLY_TYPE_USB_DCP:
- case POWER_SUPPLY_TYPE_USB_FLOAT:
typec_mode = smblib_get_prop_typec_mode(chg);
rp_ua = get_rp_based_dcp_current(chg, typec_mode);
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua);
break;
+ case POWER_SUPPLY_TYPE_USB_FLOAT:
+ /*
+ * limit ICL to 100mA, the USB driver will enumerate to check
+ * if this is a SDP and appropriately set the current
+ */
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
+ break;
case POWER_SUPPLY_TYPE_USB_HVDCP:
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 3000000);
@@ -3497,6 +3616,32 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
}
}
+static void smblib_notify_extcon_props(struct smb_charger *chg)
+{
+ union power_supply_propval val;
+
+ smblib_get_prop_typec_cc_orientation(chg, &val);
+ extcon_set_cable_state_(chg->extcon, EXTCON_USB_CC,
+ (val.intval == 2) ? 1 : 0);
+ extcon_set_cable_state_(chg->extcon, EXTCON_USB_SPEED, true);
+}
+
+static void smblib_notify_device_mode(struct smb_charger *chg, bool enable)
+{
+ if (enable)
+ smblib_notify_extcon_props(chg);
+
+ extcon_set_cable_state_(chg->extcon, EXTCON_USB, enable);
+}
+
+static void smblib_notify_usb_host(struct smb_charger *chg, bool enable)
+{
+ if (enable)
+ smblib_notify_extcon_props(chg);
+
+ extcon_set_cable_state_(chg->extcon, EXTCON_USB_HOST, enable);
+}
+
#define HVDCP_DET_MS 2500
static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
{
@@ -3516,6 +3661,8 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
if (chg->micro_usb_mode)
extcon_set_cable_state_(chg->extcon, EXTCON_USB,
true);
+ if (chg->use_extcon)
+ smblib_notify_device_mode(chg, true);
case OCP_CHARGER_BIT:
case FLOAT_CHARGER_BIT:
/* if not DCP then no hvdcp timeout happens, Enable pd here. */
@@ -3600,6 +3747,10 @@ static void typec_sink_insertion(struct smb_charger *chg)
*/
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
false, 0);
+ if (chg->use_extcon) {
+ smblib_notify_usb_host(chg, true);
+ chg->otg_present = true;
+ }
}
static void typec_sink_removal(struct smb_charger *chg)
@@ -3617,13 +3768,9 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
chg->cc2_detach_wa_active = false;
- if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
- smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
- rc = regulator_disable(chg->dpdm_reg);
- if (rc < 0)
- smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
- rc);
- }
+ rc = smblib_request_dpdm(chg, false);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
if (chg->wa_flags & BOOST_BACK_WA) {
data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
@@ -3680,6 +3827,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
chg->pd_hard_reset = 0;
chg->typec_legacy_valid = false;
+ /* write back the default FLOAT charger configuration */
+ rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
+ (u8)FLOAT_OPTIONS_MASK, chg->float_cfg);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't write float charger options rc=%d\n",
+ rc);
+
/* reset back to 120mS tCC debounce */
rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, 0);
if (rc < 0)
@@ -3745,6 +3899,14 @@ unlock:
typec_sink_removal(chg);
smblib_update_usb_type(chg);
+
+ if (chg->use_extcon) {
+ if (chg->otg_present)
+ smblib_notify_usb_host(chg, false);
+ else
+ smblib_notify_device_mode(chg, false);
+ }
+ chg->otg_present = false;
}
static void smblib_handle_typec_insertion(struct smb_charger *chg)
@@ -3759,10 +3921,14 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg)
smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
rc);
- if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT)
+ if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT) {
typec_sink_insertion(chg);
- else
+ } else {
+ rc = smblib_request_dpdm(chg, true);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
typec_sink_removal(chg);
+ }
}
static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode)
@@ -3775,6 +3941,24 @@ static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode)
return;
/*
+ * if APSD indicates FLOAT and the USB stack had detected SDP,
+ * do not respond to Rp changes as we do not confirm that its
+ * a legacy cable
+ */
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)
+ return;
+ /*
+ * We want the ICL vote @ 100mA for a FLOAT charger
+ * until the detection by the USB stack is complete.
+ * Ignore the Rp changes unless there is a
+ * pre-existing valid vote.
+ */
+ if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT &&
+ get_client_vote(chg->usb_icl_votable,
+ LEGACY_UNKNOWN_VOTER) <= 100000)
+ return;
+
+ /*
* handle Rp change for DCP/FLOAT/OCP.
* Update the current only if the Rp is different from
* the last Rp value.
@@ -3965,7 +4149,7 @@ irqreturn_t smblib_handle_wdog_bark(int irq, void *data)
if (rc < 0)
smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc);
- if (chg->step_chg_enabled)
+ if (chg->step_chg_enabled || chg->sw_jeita_enabled)
power_supply_changed(chg->batt_psy);
return IRQ_HANDLED;
@@ -4603,7 +4787,8 @@ int smblib_init(struct smb_charger *chg)
return rc;
}
- rc = qcom_step_chg_init(chg->step_chg_enabled);
+ rc = qcom_step_chg_init(chg->step_chg_enabled,
+ chg->sw_jeita_enabled);
if (rc < 0) {
smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n",
rc);
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 43a795085755..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
@@ -139,9 +140,14 @@ struct smb_irq_info {
static const unsigned int smblib_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
+ EXTCON_USB_CC,
+ EXTCON_USB_SPEED,
EXTCON_NONE,
};
+/* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */
+static const u32 smblib_extcon_exclusive[] = {0x3, 0};
+
struct smb_regulator {
struct regulator_dev *rdev;
struct regulator_desc rdesc;
@@ -306,6 +312,7 @@ struct smb_charger {
int dcp_icl_ua;
int fake_capacity;
bool step_chg_enabled;
+ bool sw_jeita_enabled;
bool is_hdc;
bool chg_done;
bool micro_usb_mode;
@@ -326,6 +333,9 @@ struct smb_charger {
int typec_mode;
int usb_icl_change_irq_enabled;
u32 jeita_status;
+ u8 float_cfg;
+ bool use_extcon;
+ bool otg_present;
/* workaround flag */
u32 wa_flags;
@@ -417,6 +427,8 @@ int smblib_get_prop_batt_current_now(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_batt_temp(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_batt_charge_counter(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_set_prop_input_suspend(struct smb_charger *chg,
const union power_supply_propval *val);
int smblib_set_prop_batt_capacity(struct smb_charger *chg,
@@ -441,11 +453,9 @@ int smblib_get_prop_usb_online(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_usb_suspend(struct smb_charger *chg,
union power_supply_propval *val);
-int smblib_get_prop_usb_voltage_now(struct smb_charger *chg,
+int smblib_get_prop_usb_voltage_max(struct smb_charger *chg,
union power_supply_propval *val);
-int smblib_get_prop_pd_current_max(struct smb_charger *chg,
- union power_supply_propval *val);
-int smblib_get_prop_usb_current_max(struct smb_charger *chg,
+int smblib_get_prop_usb_voltage_now(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_usb_current_now(struct smb_charger *chg,
union power_supply_propval *val);
@@ -473,11 +483,11 @@ int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_set_prop_pd_current_max(struct smb_charger *chg,
const union power_supply_propval *val);
-int smblib_set_prop_usb_current_max(struct smb_charger *chg,
+int smblib_set_prop_sdp_current_max(struct smb_charger *chg,
const union power_supply_propval *val);
-int smblib_set_prop_usb_voltage_min(struct smb_charger *chg,
+int smblib_set_prop_pd_voltage_max(struct smb_charger *chg,
const union power_supply_propval *val);
-int smblib_set_prop_usb_voltage_max(struct smb_charger *chg,
+int smblib_set_prop_pd_voltage_min(struct smb_charger *chg,
const union power_supply_propval *val);
int smblib_set_prop_boost_current(struct smb_charger *chg,
const union power_supply_propval *val);
@@ -499,6 +509,7 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_icl_override(struct smb_charger *chg, bool override);
int smblib_dp_dm(struct smb_charger *chg, int val);
+int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable);
int smblib_rerun_aicl(struct smb_charger *chg);
int smblib_set_icl_current(struct smb_charger *chg, int icl_ua);
int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua);
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index ec74e3825dd5..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;
@@ -215,6 +215,7 @@ static enum power_supply_property smb138x_usb_props[] = {
POWER_SUPPLY_PROP_TYPEC_MODE,
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE,
POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION,
+ POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
};
static int smb138x_usb_get_prop(struct power_supply *psy,
@@ -242,7 +243,7 @@ static int smb138x_usb_get_prop(struct power_supply *psy,
rc = smblib_get_prop_usb_voltage_now(chg, val);
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
- rc = smblib_get_prop_usb_current_max(chg, val);
+ val->intval = get_effective_result(chg->usb_icl_votable);
break;
case POWER_SUPPLY_PROP_TYPE:
val->intval = chg->usb_psy_desc.type;
@@ -256,6 +257,10 @@ static int smb138x_usb_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION:
rc = smblib_get_prop_typec_cc_orientation(chg, val);
break;
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ val->intval = get_client_vote(chg->usb_icl_votable,
+ USB_PSY_VOTER);
+ break;
default:
pr_err("get prop %d is not supported\n", prop);
return -EINVAL;
@@ -278,18 +283,12 @@ static int smb138x_usb_set_prop(struct power_supply *psy,
int rc = 0;
switch (prop) {
- case POWER_SUPPLY_PROP_VOLTAGE_MIN:
- rc = smblib_set_prop_usb_voltage_min(chg, val);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- rc = smblib_set_prop_usb_voltage_max(chg, val);
- break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- rc = smblib_set_prop_usb_current_max(chg, val);
- break;
case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE:
rc = smblib_set_prop_typec_power_role(chg, val);
break;
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ rc = smblib_set_prop_sdp_current_max(chg, val);
+ break;
default:
pr_err("set prop %d is not supported\n", prop);
return -EINVAL;
@@ -301,13 +300,6 @@ static int smb138x_usb_set_prop(struct power_supply *psy,
static int smb138x_usb_prop_is_writeable(struct power_supply *psy,
enum power_supply_property prop)
{
- switch (prop) {
- case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE:
- return 1;
- default:
- break;
- }
-
return 0;
}
@@ -735,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,
@@ -777,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,
@@ -871,6 +863,13 @@ static int smb138x_init_slave_hw(struct smb138x *chip)
return rc;
}
+ /* Disable OTG */
+ rc = smblib_masked_write(chg, CMD_OTG_REG, OTG_EN_BIT, 0);
+ if (rc < 0) {
+ pr_err("Couldn't disable OTG rc=%d\n", rc);
+ return rc;
+ }
+
/* suspend parallel charging */
rc = smb138x_set_parallel_suspend(chip, true);
if (rc < 0) {
@@ -968,6 +967,20 @@ static int smb138x_init_hw(struct smb138x *chip)
chg->dcp_icl_ua = chip->dt.usb_icl_ua;
+ /* Disable OTG */
+ rc = smblib_masked_write(chg, CMD_OTG_REG, OTG_EN_BIT, 0);
+ if (rc < 0) {
+ pr_err("Couldn't disable OTG rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Unsuspend USB input */
+ rc = smblib_masked_write(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, 0);
+ if (rc < 0) {
+ pr_err("Couldn't unsuspend USB, rc=%d\n", rc);
+ return rc;
+ }
+
/* configure to a fixed 700khz freq to avoid tdie errors */
rc = smblib_set_charge_param(chg, &chg->param.freq_buck, 700);
if (rc < 0) {
@@ -1608,14 +1621,33 @@ static int smb138x_remove(struct platform_device *pdev)
return 0;
}
+static void smb138x_shutdown(struct platform_device *pdev)
+{
+ struct smb138x *chip = platform_get_drvdata(pdev);
+ struct smb_charger *chg = &chip->chg;
+ int rc;
+
+ /* Suspend charging */
+ rc = smb138x_set_parallel_suspend(chip, true);
+ if (rc < 0)
+ pr_err("Couldn't suspend charging rc=%d\n", rc);
+
+ /* Disable OTG */
+ rc = smblib_masked_write(chg, CMD_OTG_REG, OTG_EN_BIT, 0);
+ if (rc < 0)
+ pr_err("Couldn't disable OTG rc=%d\n", rc);
+
+}
+
static struct platform_driver smb138x_driver = {
.driver = {
.name = "qcom,smb138x-charger",
.owner = THIS_MODULE,
.of_match_table = match_table,
},
- .probe = smb138x_probe,
- .remove = smb138x_remove,
+ .probe = smb138x_probe,
+ .remove = smb138x_remove,
+ .shutdown = smb138x_shutdown,
};
module_platform_driver(smb138x_driver);
diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c
index a2c08be960be..06ecc7ea6e8a 100644
--- a/drivers/power/supply/qcom/step-chg-jeita.c
+++ b/drivers/power/supply/qcom/step-chg-jeita.c
@@ -20,7 +20,7 @@
#define MAX_STEP_CHG_ENTRIES 8
#define STEP_CHG_VOTER "STEP_CHG_VOTER"
-#define STATUS_CHANGE_VOTER "STATUS_CHANGE_VOTER"
+#define JEITA_VOTER "JEITA_VOTER"
#define is_between(left, right, value) \
(((left) >= (right) && (left) >= (value) \
@@ -28,23 +28,44 @@
|| ((left) <= (right) && (left) <= (value) \
&& (value) <= (right)))
-struct step_chg_data {
- u32 vbatt_soc_low;
- u32 vbatt_soc_high;
- u32 fcc_ua;
+struct range_data {
+ u32 low_threshold;
+ u32 high_threshold;
+ u32 value;
};
struct step_chg_cfg {
- u32 psy_prop;
- char *prop_name;
- struct step_chg_data cfg[MAX_STEP_CHG_ENTRIES];
+ u32 psy_prop;
+ char *prop_name;
+ int hysteresis;
+ struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES];
+};
+
+struct jeita_fcc_cfg {
+ u32 psy_prop;
+ char *prop_name;
+ int hysteresis;
+ struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES];
+};
+
+struct jeita_fv_cfg {
+ u32 psy_prop;
+ char *prop_name;
+ int hysteresis;
+ struct range_data fv_cfg[MAX_STEP_CHG_ENTRIES];
};
struct step_chg_info {
- ktime_t last_update_time;
+ ktime_t step_last_update_time;
+ ktime_t jeita_last_update_time;
bool step_chg_enable;
+ bool sw_jeita_enable;
+ int jeita_fcc_index;
+ int jeita_fv_index;
+ int step_index;
struct votable *fcc_votable;
+ struct votable *fv_votable;
struct wakeup_source *step_chg_ws;
struct power_supply *batt_psy;
struct delayed_work status_change_work;
@@ -53,32 +74,70 @@ struct step_chg_info {
static struct step_chg_info *the_chip;
+#define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */
+
/*
* Step Charging Configuration
* Update the table based on the battery profile
* Supports VBATT and SOC based source
+ * range data must be in increasing ranges and shouldn't overlap
*/
static struct step_chg_cfg step_chg_config = {
- .psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW,
- .prop_name = "VBATT",
- .cfg = {
+ .psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ .prop_name = "VBATT",
+ .hysteresis = 100000, /* 100mV */
+ .fcc_cfg = {
/* VBAT_LOW VBAT_HIGH FCC */
{3600000, 4000000, 3000000},
- {4000000, 4200000, 2800000},
- {4200000, 4400000, 2000000},
+ {4001000, 4200000, 2800000},
+ {4201000, 4400000, 2000000},
},
+ /*
+ * SOC STEP-CHG configuration example.
+ *
+ * .psy_prop = POWER_SUPPLY_PROP_CAPACITY,
+ * .prop_name = "SOC",
+ * .fcc_cfg = {
+ * //SOC_LOW SOC_HIGH FCC
+ * {20, 70, 3000000},
+ * {70, 90, 2750000},
+ * {90, 100, 2500000},
+ * },
+ */
+};
+
/*
- * SOC STEP-CHG configuration example.
- *
- * .psy_prop = POWER_SUPPLY_PROP_CAPACITY,
- * .prop_name = "SOC",
- * .cfg = {
- * //SOC_LOW SOC_HIGH FCC
- * {20, 70, 3000000},
- * {70, 90, 2750000},
- * {90, 100, 2500000},
- * },
+ * Jeita Charging Configuration
+ * Update the table based on the battery profile
+ * Please ensure that the TEMP ranges are programmed in the hw so that
+ * an interrupt is issued and a consequent psy changed will cause us to
+ * react immediately.
+ * range data must be in increasing ranges and shouldn't overlap.
+ * Gaps are okay
*/
+static struct jeita_fcc_cfg jeita_fcc_config = {
+ .psy_prop = POWER_SUPPLY_PROP_TEMP,
+ .prop_name = "BATT_TEMP",
+ .hysteresis = 10, /* 1degC hysteresis */
+ .fcc_cfg = {
+ /* TEMP_LOW TEMP_HIGH FCC */
+ {0, 100, 600000},
+ {101, 200, 2000000},
+ {201, 450, 3000000},
+ {451, 550, 600000},
+ },
+};
+
+static struct jeita_fv_cfg jeita_fv_config = {
+ .psy_prop = POWER_SUPPLY_PROP_TEMP,
+ .prop_name = "BATT_TEMP",
+ .hysteresis = 10, /* 1degC hysteresis */
+ .fv_cfg = {
+ /* TEMP_LOW TEMP_HIGH FCC */
+ {0, 100, 4200000},
+ {101, 450, 4400000},
+ {451, 550, 4200000},
+ },
};
static bool is_batt_available(struct step_chg_info *chip)
@@ -92,22 +151,67 @@ static bool is_batt_available(struct step_chg_info *chip)
return true;
}
-static int get_fcc(int threshold)
+static int get_val(struct range_data *range, int hysteresis, int current_index,
+ int threshold,
+ int *new_index, int *val)
{
int i;
+ *new_index = -EINVAL;
+ /* first find the matching index without hysteresis */
for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
- if (is_between(step_chg_config.cfg[i].vbatt_soc_low,
- step_chg_config.cfg[i].vbatt_soc_high, threshold))
- return step_chg_config.cfg[i].fcc_ua;
+ if (is_between(range[i].low_threshold,
+ range[i].high_threshold, threshold)) {
+ *new_index = i;
+ *val = range[i].value;
+ }
+
+ /* if nothing was found, return -ENODATA */
+ if (*new_index == -EINVAL)
+ return -ENODATA;
+ /*
+ * If we don't have a current_index return this
+ * newfound value. There is no hysterisis from out of range
+ * to in range transition
+ */
+ if (current_index == -EINVAL)
+ return 0;
- return -ENODATA;
+ /*
+ * Check for hysteresis if it in the neighbourhood
+ * of our current index.
+ */
+ if (*new_index == current_index + 1) {
+ if (threshold < range[*new_index].low_threshold + hysteresis) {
+ /*
+ * Stay in the current index, threshold is not higher
+ * by hysteresis amount
+ */
+ *new_index = current_index;
+ *val = range[current_index].value;
+ }
+ } else if (*new_index == current_index - 1) {
+ if (threshold > range[*new_index].high_threshold - hysteresis) {
+ /*
+ * stay in the current index, threshold is not lower
+ * by hysteresis amount
+ */
+ *new_index = current_index;
+ *val = range[current_index].value;
+ }
+ }
+ return 0;
}
static int handle_step_chg_config(struct step_chg_info *chip)
{
union power_supply_propval pval = {0, };
int rc = 0, fcc_ua = 0;
+ u64 elapsed_us;
+
+ elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time);
+ if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
+ goto reschedule;
rc = power_supply_get_property(chip->batt_psy,
POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, &pval);
@@ -119,7 +223,7 @@ static int handle_step_chg_config(struct step_chg_info *chip)
if (!chip->step_chg_enable) {
if (chip->fcc_votable)
vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
- return 0;
+ goto update_time;
}
rc = power_supply_get_property(chip->batt_psy,
@@ -130,48 +234,151 @@ static int handle_step_chg_config(struct step_chg_info *chip)
return rc;
}
- chip->fcc_votable = find_votable("FCC");
- if (!chip->fcc_votable)
- return -EINVAL;
-
- fcc_ua = get_fcc(pval.intval);
- if (fcc_ua < 0) {
+ rc = get_val(step_chg_config.fcc_cfg, step_chg_config.hysteresis,
+ chip->step_index,
+ pval.intval,
+ &chip->step_index,
+ &fcc_ua);
+ if (rc < 0) {
/* remove the vote if no step-based fcc is found */
- vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
- return 0;
+ if (chip->fcc_votable)
+ vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
+ goto update_time;
}
+ if (!chip->fcc_votable)
+ chip->fcc_votable = find_votable("FCC");
+ if (!chip->fcc_votable)
+ return -EINVAL;
+
vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua);
pr_debug("%s = %d Step-FCC = %duA\n",
step_chg_config.prop_name, pval.intval, fcc_ua);
+update_time:
+ chip->step_last_update_time = ktime_get();
return 0;
+
+reschedule:
+ /* reschedule 1000uS after the remaining time */
+ return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
+}
+
+static int handle_jeita(struct step_chg_info *chip)
+{
+ union power_supply_propval pval = {0, };
+ int rc = 0, fcc_ua = 0, fv_uv = 0;
+ u64 elapsed_us;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_SW_JEITA_ENABLED, &pval);
+ if (rc < 0)
+ chip->sw_jeita_enable = 0;
+ else
+ chip->sw_jeita_enable = pval.intval;
+
+ if (!chip->sw_jeita_enable) {
+ if (chip->fcc_votable)
+ vote(chip->fcc_votable, JEITA_VOTER, false, 0);
+ if (chip->fv_votable)
+ vote(chip->fv_votable, JEITA_VOTER, false, 0);
+ return 0;
+ }
+
+ elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time);
+ if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
+ goto reschedule;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ jeita_fcc_config.psy_prop, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't read %s property rc=%d\n",
+ step_chg_config.prop_name, rc);
+ return rc;
+ }
+
+ rc = get_val(jeita_fcc_config.fcc_cfg, jeita_fcc_config.hysteresis,
+ chip->jeita_fcc_index,
+ pval.intval,
+ &chip->jeita_fcc_index,
+ &fcc_ua);
+ if (rc < 0) {
+ /* remove the vote if no step-based fcc is found */
+ if (chip->fcc_votable)
+ vote(chip->fcc_votable, JEITA_VOTER, false, 0);
+ goto update_time;
+ }
+
+ if (!chip->fcc_votable)
+ chip->fcc_votable = find_votable("FCC");
+ if (!chip->fcc_votable)
+ /* changing FCC is a must */
+ return -EINVAL;
+
+ vote(chip->fcc_votable, JEITA_VOTER, true, fcc_ua);
+
+ rc = get_val(jeita_fv_config.fv_cfg, jeita_fv_config.hysteresis,
+ chip->jeita_fv_index,
+ pval.intval,
+ &chip->jeita_fv_index,
+ &fv_uv);
+ if (rc < 0) {
+ /* remove the vote if no step-based fcc is found */
+ if (chip->fv_votable)
+ vote(chip->fv_votable, JEITA_VOTER, false, 0);
+ goto update_time;
+ }
+
+ chip->fv_votable = find_votable("FV");
+ if (!chip->fv_votable)
+ goto update_time;
+
+ vote(chip->fv_votable, JEITA_VOTER, true, fv_uv);
+
+ pr_debug("%s = %d FCC = %duA FV = %duV\n",
+ step_chg_config.prop_name, pval.intval, fcc_ua, fv_uv);
+
+update_time:
+ chip->jeita_last_update_time = ktime_get();
+ return 0;
+
+reschedule:
+ /* reschedule 1000uS after the remaining time */
+ return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
}
-#define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */
static void status_change_work(struct work_struct *work)
{
struct step_chg_info *chip = container_of(work,
struct step_chg_info, status_change_work.work);
int rc = 0;
- u64 elapsed_us;
-
- elapsed_us = ktime_us_delta(ktime_get(), chip->last_update_time);
- if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
- goto release_ws;
+ int reschedule_us;
+ int reschedule_jeita_work_us = 0;
+ int reschedule_step_work_us = 0;
if (!is_batt_available(chip))
- goto release_ws;
+ return;
+
+ /* skip elapsed_us debounce for handling battery temperature */
+ rc = handle_jeita(chip);
+ if (rc > 0)
+ reschedule_jeita_work_us = rc;
+ else if (rc < 0)
+ pr_err("Couldn't handle sw jeita rc = %d\n", rc);
rc = handle_step_chg_config(chip);
+ if (rc > 0)
+ reschedule_step_work_us = rc;
if (rc < 0)
- goto release_ws;
-
- chip->last_update_time = ktime_get();
+ pr_err("Couldn't handle step rc = %d\n", rc);
-release_ws:
- __pm_relax(chip->step_chg_ws);
+ reschedule_us = min(reschedule_jeita_work_us, reschedule_step_work_us);
+ if (reschedule_us == 0)
+ __pm_relax(chip->step_chg_ws);
+ else
+ schedule_delayed_work(&chip->status_change_work,
+ usecs_to_jiffies(reschedule_us));
}
static int step_chg_notifier_call(struct notifier_block *nb,
@@ -205,7 +412,7 @@ static int step_chg_register_notifier(struct step_chg_info *chip)
return 0;
}
-int qcom_step_chg_init(bool step_chg_enable)
+int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable)
{
int rc;
struct step_chg_info *chip;
@@ -226,12 +433,34 @@ int qcom_step_chg_init(bool step_chg_enable)
}
chip->step_chg_enable = step_chg_enable;
+ chip->sw_jeita_enable = sw_jeita_enable;
+
+ chip->step_index = -EINVAL;
+ chip->jeita_fcc_index = -EINVAL;
+ chip->jeita_fv_index = -EINVAL;
if (step_chg_enable && (!step_chg_config.psy_prop ||
!step_chg_config.prop_name)) {
/* fail if step-chg configuration is invalid */
pr_err("Step-chg configuration not defined - fail\n");
- 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");
+ rc = -ENODATA;
+ goto release_wakeup_source;
+ }
+
+ if (sw_jeita_enable && (!jeita_fv_config.psy_prop ||
+ !jeita_fv_config.prop_name)) {
+ /* fail if step-chg configuration is invalid */
+ pr_err("Jeita TEMP configuration not defined - fail\n");
+ rc = -ENODATA;
+ goto release_wakeup_source;
}
INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
diff --git a/drivers/power/supply/qcom/step-chg-jeita.h b/drivers/power/supply/qcom/step-chg-jeita.h
index 928eeb7a670b..53335c3c2c70 100644
--- a/drivers/power/supply/qcom/step-chg-jeita.h
+++ b/drivers/power/supply/qcom/step-chg-jeita.h
@@ -12,6 +12,6 @@
#ifndef __STEP_CHG_H__
#define __STEP_CHG_H__
-int qcom_step_chg_init(bool);
+int qcom_step_chg_init(bool, bool);
void qcom_step_chg_deinit(void);
#endif /* __STEP_CHG_H__ */
diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c
index e46b1d583f40..2531b74b4588 100644
--- a/drivers/pwm/pwm-qpnp.c
+++ b/drivers/pwm/pwm-qpnp.c
@@ -328,6 +328,7 @@ struct qpnp_pwm_chip {
bool enabled;
struct _qpnp_pwm_config pwm_config;
struct qpnp_lpg_config lpg_config;
+ enum pm_pwm_mode pwm_mode;
spinlock_t lpg_lock;
enum qpnp_lpg_revision revision;
u8 sub_type;
@@ -1314,12 +1315,10 @@ after_table_write:
return rc;
}
+/* lpg_lock should be held while calling _pwm_enable() */
static int _pwm_enable(struct qpnp_pwm_chip *chip)
{
int rc = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lpg_lock, flags);
if (QPNP_IS_PWM_CONFIG_SELECTED(
chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]) ||
@@ -1332,8 +1331,21 @@ static int _pwm_enable(struct qpnp_pwm_chip *chip)
if (!rc)
chip->enabled = true;
- spin_unlock_irqrestore(&chip->lpg_lock, flags);
+ return rc;
+}
+
+/* lpg_lock should be held while calling _pwm_change_mode() */
+static int _pwm_change_mode(struct qpnp_pwm_chip *chip, enum pm_pwm_mode mode)
+{
+ int rc;
+ if (mode == PM_PWM_MODE_LPG)
+ rc = qpnp_configure_lpg_control(chip);
+ else
+ rc = qpnp_configure_pwm_control(chip);
+
+ if (rc)
+ pr_err("Failed to change the mode\n");
return rc;
}
@@ -1410,11 +1422,15 @@ static int qpnp_pwm_enable(struct pwm_chip *pwm_chip,
{
int rc;
struct qpnp_pwm_chip *chip = qpnp_pwm_from_pwm_chip(pwm_chip);
+ unsigned long flags;
+ spin_lock_irqsave(&chip->lpg_lock, flags);
rc = _pwm_enable(chip);
if (rc)
pr_err("Failed to enable PWM channel: %d\n", chip->channel_id);
+ spin_unlock_irqrestore(&chip->lpg_lock, flags);
+
return rc;
}
@@ -1452,20 +1468,6 @@ static void qpnp_pwm_disable(struct pwm_chip *pwm_chip,
chip->channel_id);
}
-static int _pwm_change_mode(struct qpnp_pwm_chip *chip, enum pm_pwm_mode mode)
-{
- int rc;
-
- if (mode)
- rc = qpnp_configure_lpg_control(chip);
- else
- rc = qpnp_configure_pwm_control(chip);
-
- if (rc)
- pr_err("Failed to change the mode\n");
- return rc;
-}
-
/**
* pwm_change_mode - Change the PWM mode configuration
* @pwm: the PWM device
@@ -1490,7 +1492,22 @@ int pwm_change_mode(struct pwm_device *pwm, enum pm_pwm_mode mode)
chip = qpnp_pwm_from_pwm_dev(pwm);
spin_lock_irqsave(&chip->lpg_lock, flags);
- rc = _pwm_change_mode(chip, mode);
+ if (chip->pwm_mode != mode) {
+ rc = _pwm_change_mode(chip, mode);
+ if (rc) {
+ pr_err("Failed to change mode: %d, rc=%d\n", mode, rc);
+ goto unlock;
+ }
+ chip->pwm_mode = mode;
+ if (chip->enabled) {
+ rc = _pwm_enable(chip);
+ if (rc) {
+ pr_err("Failed to enable PWM, rc=%d\n", rc);
+ goto unlock;
+ }
+ }
+ }
+unlock:
spin_unlock_irqrestore(&chip->lpg_lock, flags);
return rc;
@@ -1894,7 +1911,7 @@ out:
static int qpnp_parse_dt_config(struct platform_device *pdev,
struct qpnp_pwm_chip *chip)
{
- int rc, enable, lut_entry_size, list_size, i;
+ int rc, mode, lut_entry_size, list_size, i;
const char *label;
const __be32 *prop;
u32 size;
@@ -2078,18 +2095,20 @@ static int qpnp_parse_dt_config(struct platform_device *pdev,
}
}
- rc = of_property_read_u32(of_node, "qcom,mode-select", &enable);
+ rc = of_property_read_u32(of_node, "qcom,mode-select", &mode);
if (rc)
goto read_opt_props;
- if ((enable == PM_PWM_MODE_PWM && found_pwm_subnode == 0) ||
- (enable == PM_PWM_MODE_LPG && found_lpg_subnode == 0)) {
+ if (mode > PM_PWM_MODE_LPG ||
+ (mode == PM_PWM_MODE_PWM && found_pwm_subnode == 0) ||
+ (mode == PM_PWM_MODE_LPG && found_lpg_subnode == 0)) {
dev_err(&pdev->dev, "%s: Invalid mode select\n", __func__);
rc = -EINVAL;
goto out;
}
- _pwm_change_mode(chip, enable);
+ chip->pwm_mode = mode;
+ _pwm_change_mode(chip, mode);
_pwm_enable(chip);
read_opt_props:
diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c
index ecf7885a4bff..4d6d63e6d887 100644
--- a/drivers/regulator/cprh-kbss-regulator.c
+++ b/drivers/regulator/cprh-kbss-regulator.c
@@ -80,7 +80,7 @@ struct cprh_kbss_fuses {
* Fuse combos 24 - 31 map to CPR fusing revision 0 - 7 with speed bin fuse = 3.
*/
#define CPRH_MSM8998_KBSS_FUSE_COMBO_COUNT 32
-#define CPRH_SDM660_KBSS_FUSE_COMBO_COUNT 16
+#define CPRH_SDM660_KBSS_FUSE_COMBO_COUNT 32
#define CPRH_SDM630_KBSS_FUSE_COMBO_COUNT 24
/*
@@ -1069,6 +1069,12 @@ static int cprh_kbss_calculate_open_loop_voltages(struct cpr3_regulator *vreg)
CPRH_KBSS_FUSE_STEP_VOLT, fuse->init_voltage[i],
CPRH_KBSS_VOLTAGE_FUSE_SIZE);
+ /* SDM660 speed bin #3 does not support TURBO_L1/L2 */
+ if (soc_revision == SDM660_SOC_ID && vreg->speed_bin_fuse == 3
+ && (id == CPRH_KBSS_PERFORMANCE_CLUSTER_ID)
+ && (i == CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO_L2))
+ continue;
+
/* Log fused open-loop voltage values for debugging purposes. */
cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", corner_name[i],
fuse_volt[i]);
@@ -1615,6 +1621,11 @@ static int cprh_kbss_calculate_target_quotients(struct cpr3_regulator *vreg)
CPRH_SDM660_PERF_KBSS_FUSE_CORNER_SVS;
highest_fuse_corner =
CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO_L2;
+
+ /* speed-bin 3 does not have Turbo_L2 fuse */
+ if (vreg->speed_bin_fuse == 3)
+ highest_fuse_corner =
+ CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO;
}
break;
case SDM630_SOC_ID:
diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c
index 724e8a4c00da..27f2d9851c2a 100644
--- a/drivers/regulator/qpnp-lcdb-regulator.c
+++ b/drivers/regulator/qpnp-lcdb-regulator.c
@@ -147,6 +147,8 @@
#define MIN_SOFT_START_US 0
#define MAX_SOFT_START_US 2000
+#define BST_HEADROOM_DEFAULT_MV 200
+
struct ldo_regulator {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
@@ -187,6 +189,7 @@ struct bst_params {
int soft_start_us;
int vreg_ok_dbc_us;
int voltage_mv;
+ u16 headroom_mv;
};
struct qpnp_lcdb {
@@ -853,28 +856,40 @@ irq_handled:
#define VOLTAGE_STEP_50_MV 50
#define VOLTAGE_STEP_50MV_OFFSET 0xA
static int qpnp_lcdb_set_bst_voltage(struct qpnp_lcdb *lcdb,
- int voltage_mv)
+ int voltage_mv, u8 type)
{
int rc = 0;
u8 val = 0;
+ int bst_voltage_mv;
+ struct ldo_regulator *ldo = &lcdb->ldo;
+ struct ncp_regulator *ncp = &lcdb->ncp;
+ struct bst_params *bst = &lcdb->bst;
+
+ /* Vout_Boost = headroom_mv + max( Vout_LDO, abs (Vout_NCP)) */
+ bst_voltage_mv = max(voltage_mv, max(ldo->voltage_mv, ncp->voltage_mv));
+ bst_voltage_mv += bst->headroom_mv;
+
+ if (bst_voltage_mv < MIN_BST_VOLTAGE_MV)
+ bst_voltage_mv = MIN_BST_VOLTAGE_MV;
+ else if (bst_voltage_mv > MAX_BST_VOLTAGE_MV)
+ bst_voltage_mv = MAX_BST_VOLTAGE_MV;
+
+ if (bst_voltage_mv != bst->voltage_mv) {
+ val = DIV_ROUND_UP(bst_voltage_mv - MIN_BST_VOLTAGE_MV,
+ VOLTAGE_STEP_50_MV);
- if (voltage_mv < MIN_BST_VOLTAGE_MV)
- voltage_mv = MIN_BST_VOLTAGE_MV;
- else if (voltage_mv > MAX_BST_VOLTAGE_MV)
- voltage_mv = MAX_BST_VOLTAGE_MV;
-
- val = DIV_ROUND_UP(voltage_mv - MIN_BST_VOLTAGE_MV,
- VOLTAGE_STEP_50_MV);
-
- rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
- LCDB_BST_OUTPUT_VOLTAGE_REG,
- SET_OUTPUT_VOLTAGE_MASK, val);
- if (rc < 0)
- pr_err("Failed to set boost voltage %d mv rc=%d\n",
- voltage_mv, rc);
- else
- pr_debug("Boost voltage set = %d mv (0x%02x = 0x%02x)\n",
- voltage_mv, LCDB_BST_OUTPUT_VOLTAGE_REG, val);
+ rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+ LCDB_BST_OUTPUT_VOLTAGE_REG,
+ SET_OUTPUT_VOLTAGE_MASK, val);
+ if (rc < 0) {
+ pr_err("Failed to set boost voltage %d mv rc=%d\n",
+ bst_voltage_mv, rc);
+ } else {
+ pr_debug("Boost voltage set = %d mv (0x%02x = 0x%02x)\n",
+ bst_voltage_mv, LCDB_BST_OUTPUT_VOLTAGE_REG, val);
+ bst->voltage_mv = bst_voltage_mv;
+ }
+ }
return rc;
}
@@ -905,25 +920,16 @@ static int qpnp_lcdb_set_voltage(struct qpnp_lcdb *lcdb,
u16 offset = LCDB_LDO_OUTPUT_VOLTAGE_REG;
u8 val = 0;
- if (type == BST)
- return qpnp_lcdb_set_bst_voltage(lcdb, voltage_mv);
-
- if (type == NCP)
- offset = LCDB_NCP_OUTPUT_VOLTAGE_REG;
-
if (!is_between(voltage_mv, MIN_VOLTAGE_MV, MAX_VOLTAGE_MV)) {
pr_err("Invalid voltage %dmv (min=%d max=%d)\n",
voltage_mv, MIN_VOLTAGE_MV, MAX_VOLTAGE_MV);
return -EINVAL;
}
- /* Change the BST voltage to LDO + 100mV */
- if (type == LDO) {
- rc = qpnp_lcdb_set_bst_voltage(lcdb, voltage_mv + 100);
- if (rc < 0) {
- pr_err("Failed to set boost voltage rc=%d\n", rc);
- return rc;
- }
+ rc = qpnp_lcdb_set_bst_voltage(lcdb, voltage_mv, type);
+ if (rc < 0) {
+ pr_err("Failed to set boost voltage rc=%d\n", rc);
+ return rc;
}
/* Below logic is only valid for LDO and NCP type */
@@ -936,6 +942,9 @@ static int qpnp_lcdb_set_voltage(struct qpnp_lcdb *lcdb,
val += VOLTAGE_STEP_50MV_OFFSET;
}
+ if (type == NCP)
+ offset = LCDB_NCP_OUTPUT_VOLTAGE_REG;
+
rc = qpnp_lcdb_masked_write(lcdb, lcdb->base + offset,
SET_OUTPUT_VOLTAGE_MASK, val);
if (rc < 0)
@@ -1058,6 +1067,8 @@ static int qpnp_lcdb_ldo_regulator_set_voltage(struct regulator_dev *rdev,
rc = qpnp_lcdb_set_voltage(lcdb, min_uV / 1000, LDO);
if (rc < 0)
pr_err("Failed to set LDO voltage rc=%c\n", rc);
+ else
+ lcdb->ldo.voltage_mv = min_uV / 1000;
return rc;
}
@@ -1129,6 +1140,8 @@ static int qpnp_lcdb_ncp_regulator_set_voltage(struct regulator_dev *rdev,
rc = qpnp_lcdb_set_voltage(lcdb, min_uV / 1000, NCP);
if (rc < 0)
pr_err("Failed to set LDO voltage rc=%c\n", rc);
+ else
+ lcdb->ncp.voltage_mv = min_uV / 1000;
return rc;
}
@@ -1389,6 +1402,12 @@ static int qpnp_lcdb_bst_dt_init(struct qpnp_lcdb *lcdb)
return -EINVAL;
}
+ /* Boost head room configuration */
+ of_property_read_u16(node, "qcom,bst-headroom-mv",
+ &lcdb->bst.headroom_mv);
+ if (lcdb->bst.headroom_mv < BST_HEADROOM_DEFAULT_MV)
+ lcdb->bst.headroom_mv = BST_HEADROOM_DEFAULT_MV;
+
return 0;
}
@@ -1695,6 +1714,9 @@ static int qpnp_lcdb_init_bst(struct qpnp_lcdb *lcdb)
}
lcdb->bst.soft_start_us = (val & SOFT_START_MASK) * 200 + 200;
+ if (!lcdb->bst.headroom_mv)
+ lcdb->bst.headroom_mv = BST_HEADROOM_DEFAULT_MV;
+
return 0;
}
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index ce129e595b55..5c935847599c 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -248,6 +248,7 @@ struct fnic {
struct completion *remove_wait; /* device remove thread blocks */
atomic_t in_flight; /* io counter */
+ bool internal_reset_inprogress;
u32 _reserved; /* fill hole */
unsigned long state_flags; /* protected by host lock */
enum fnic_state state;
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index 266b909fe854..82e4bc8c11c5 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -2533,6 +2533,19 @@ int fnic_host_reset(struct scsi_cmnd *sc)
unsigned long wait_host_tmo;
struct Scsi_Host *shost = sc->device->host;
struct fc_lport *lp = shost_priv(shost);
+ struct fnic *fnic = lport_priv(lp);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->internal_reset_inprogress == 0) {
+ fnic->internal_reset_inprogress = 1;
+ } else {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "host reset in progress skipping another host reset\n");
+ return SUCCESS;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
/*
* If fnic_reset is successful, wait for fabric login to complete
@@ -2553,6 +2566,9 @@ int fnic_host_reset(struct scsi_cmnd *sc)
}
}
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ fnic->internal_reset_inprogress = 0;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
return ret;
}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index 5b2c37f1e908..9b5367294116 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -4981,15 +4981,14 @@ _base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag,
static int
_base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
{
- int r, i;
+ int r, i, index;
unsigned long flags;
u32 reply_address;
u16 smid;
struct _tr_list *delayed_tr, *delayed_tr_next;
u8 hide_flag;
struct adapter_reply_queue *reply_q;
- long reply_post_free;
- u32 reply_post_free_sz, index = 0;
+ Mpi2ReplyDescriptorsUnion_t *reply_post_free_contig;
dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
__func__));
@@ -5061,27 +5060,27 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
_base_assign_reply_queues(ioc);
/* initialize Reply Post Free Queue */
- reply_post_free_sz = ioc->reply_post_queue_depth *
- sizeof(Mpi2DefaultReplyDescriptor_t);
- reply_post_free = (long)ioc->reply_post[index].reply_post_free;
+ index = 0;
+ reply_post_free_contig = ioc->reply_post[0].reply_post_free;
list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ /*
+ * If RDPQ is enabled, switch to the next allocation.
+ * Otherwise advance within the contiguous region.
+ */
+ if (ioc->rdpq_array_enable) {
+ reply_q->reply_post_free =
+ ioc->reply_post[index++].reply_post_free;
+ } else {
+ reply_q->reply_post_free = reply_post_free_contig;
+ reply_post_free_contig += ioc->reply_post_queue_depth;
+ }
+
reply_q->reply_post_host_index = 0;
- reply_q->reply_post_free = (Mpi2ReplyDescriptorsUnion_t *)
- reply_post_free;
for (i = 0; i < ioc->reply_post_queue_depth; i++)
reply_q->reply_post_free[i].Words =
cpu_to_le64(ULLONG_MAX);
if (!_base_is_controller_msix_enabled(ioc))
goto skip_init_reply_post_free_queue;
- /*
- * If RDPQ is enabled, switch to the next allocation.
- * Otherwise advance within the contiguous region.
- */
- if (ioc->rdpq_array_enable)
- reply_post_free = (long)
- ioc->reply_post[++index].reply_post_free;
- else
- reply_post_free += reply_post_free_sz;
}
skip_init_reply_post_free_queue:
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 6b942d9e5b74..1ed85dfc008d 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -329,12 +329,15 @@ qla2x00_sysfs_read_optrom(struct file *filp, struct kobject *kobj,
struct qla_hw_data *ha = vha->hw;
ssize_t rval = 0;
+ mutex_lock(&ha->optrom_mutex);
+
if (ha->optrom_state != QLA_SREADING)
- return 0;
+ goto out;
- mutex_lock(&ha->optrom_mutex);
rval = memory_read_from_buffer(buf, count, &off, ha->optrom_buffer,
ha->optrom_region_size);
+
+out:
mutex_unlock(&ha->optrom_mutex);
return rval;
@@ -349,14 +352,19 @@ qla2x00_sysfs_write_optrom(struct file *filp, struct kobject *kobj,
struct device, kobj)));
struct qla_hw_data *ha = vha->hw;
- if (ha->optrom_state != QLA_SWRITING)
+ mutex_lock(&ha->optrom_mutex);
+
+ if (ha->optrom_state != QLA_SWRITING) {
+ mutex_unlock(&ha->optrom_mutex);
return -EINVAL;
- if (off > ha->optrom_region_size)
+ }
+ if (off > ha->optrom_region_size) {
+ mutex_unlock(&ha->optrom_mutex);
return -ERANGE;
+ }
if (off + count > ha->optrom_region_size)
count = ha->optrom_region_size - off;
- mutex_lock(&ha->optrom_mutex);
memcpy(&ha->optrom_buffer[off], buf, count);
mutex_unlock(&ha->optrom_mutex);
diff --git a/drivers/scsi/snic/snic_main.c b/drivers/scsi/snic/snic_main.c
index 2b3c25371d76..8175f997e82c 100644
--- a/drivers/scsi/snic/snic_main.c
+++ b/drivers/scsi/snic/snic_main.c
@@ -584,6 +584,7 @@ snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!pool) {
SNIC_HOST_ERR(shost, "dflt sgl pool creation failed\n");
+ ret = -ENOMEM;
goto err_free_res;
}
@@ -594,6 +595,7 @@ snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!pool) {
SNIC_HOST_ERR(shost, "max sgl pool creation failed\n");
+ ret = -ENOMEM;
goto err_free_dflt_sgl_pool;
}
@@ -604,6 +606,7 @@ snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!pool) {
SNIC_HOST_ERR(shost, "snic tmreq info pool creation failed.\n");
+ ret = -ENOMEM;
goto err_free_max_sgl_pool;
}
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index aae796678ffe..47106f937371 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -2416,7 +2416,8 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
*/
static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg)
{
- int ret = 0, scm_ret = 0;
+ int ret = 0;
+ u64 scm_ret = 0;
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
/* scm command buffer structrue */
@@ -2457,7 +2458,7 @@ static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg)
cbuf.device_id = UFS_TZ_DEV_ID;
ret = scm_restore_sec_cfg(cbuf.device_id, cbuf.spare, &scm_ret);
if (ret || scm_ret) {
- dev_dbg(hba->dev, "%s: failed, ret %d scm_ret %d\n",
+ dev_dbg(hba->dev, "%s: failed, ret %d scm_ret %llu\n",
__func__, ret, scm_ret);
if (!ret)
ret = scm_ret;
@@ -2466,7 +2467,7 @@ static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg)
}
out:
- dev_dbg(hba->dev, "%s: ip: restore_sec_cfg %d, op: restore_sec_cfg %d, ret %d scm_ret %d\n",
+ dev_dbg(hba->dev, "%s: ip: restore_sec_cfg %d, op: restore_sec_cfg %d, ret %d scm_ret %llu\n",
__func__, restore_sec_cfg, host->sec_cfg_updated, ret, scm_ret);
return ret;
}
diff --git a/drivers/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_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 37193bbb23b7..042108d4035b 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -2242,6 +2242,7 @@ static int parse_qos_dt_params(struct device_node *node,
einfo->ramp_time_us[i] = arr32[i];
rc = 0;
+ kfree(arr32);
return rc;
invalid_key:
diff --git a/drivers/soc/qcom/glink_spi_xprt.c b/drivers/soc/qcom/glink_spi_xprt.c
index 6794c30605d7..a94c4c909a40 100644
--- a/drivers/soc/qcom/glink_spi_xprt.c
+++ b/drivers/soc/qcom/glink_spi_xprt.c
@@ -121,6 +121,8 @@ struct glink_cmpnt {
* @tx_fifo_write_reg_addr: Address of the TX FIFO Write Index Register.
* @rx_fifo_read_reg_addr: Address of the RX FIFO Read Index Register.
* @rx_fifo_write_reg_addr: Address of the RX FIFO Write Index Register.
+ * @tx_fifo_write: Internal write index for TX FIFO.
+ * @rx_fifo_read: Internal read index for RX FIFO.
* @kwork: Work to be executed when receiving data.
* @kworker: Handle to the entity processing @kwork.
* @task: Handle to the task context that runs @kworker.
@@ -158,6 +160,8 @@ struct edge_info {
unsigned int tx_fifo_write_reg_addr;
unsigned int rx_fifo_read_reg_addr;
unsigned int rx_fifo_write_reg_addr;
+ uint32_t tx_fifo_write;
+ uint32_t rx_fifo_read;
struct kthread_work kwork;
struct kthread_worker kworker;
@@ -368,6 +372,19 @@ static int glink_spi_xprt_write_avail(struct edge_info *einfo)
int write_avail;
int ret;
+ if (unlikely(!einfo->tx_fifo_start)) {
+ ret = glink_spi_xprt_reg_read(einfo,
+ einfo->tx_fifo_write_reg_addr, &einfo->tx_fifo_write);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->tx_fifo_write_reg_addr);
+ return 0;
+ }
+ einfo->tx_fifo_start = einfo->tx_fifo_write;
+ }
+ write_id = einfo->tx_fifo_write;
+
ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_read_reg_addr,
&read_id);
if (ret < 0) {
@@ -377,21 +394,9 @@ static int glink_spi_xprt_write_avail(struct edge_info *einfo)
return 0;
}
- ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_write_reg_addr,
- &write_id);
- if (ret < 0) {
- pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n",
- __func__, ret, einfo->xprt_cfg.edge,
- einfo->tx_fifo_write_reg_addr);
- return 0;
- }
-
if (!read_id || !write_id)
return 0;
- if (unlikely(!einfo->tx_fifo_start))
- einfo->tx_fifo_start = write_id;
-
if (read_id > write_id)
write_avail = read_id - write_id;
else
@@ -421,14 +426,18 @@ static int glink_spi_xprt_read_avail(struct edge_info *einfo)
int read_avail;
int ret;
- ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_read_reg_addr,
- &read_id);
- if (ret < 0) {
- pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n",
- __func__, ret, einfo->xprt_cfg.edge,
- einfo->rx_fifo_read_reg_addr);
- return 0;
+ if (unlikely(!einfo->rx_fifo_start)) {
+ ret = glink_spi_xprt_reg_read(einfo,
+ einfo->rx_fifo_read_reg_addr, &einfo->rx_fifo_read);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->rx_fifo_read_reg_addr);
+ return 0;
+ }
+ einfo->rx_fifo_start = einfo->rx_fifo_read;
}
+ read_id = einfo->rx_fifo_read;
ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_write_reg_addr,
&write_id);
@@ -442,9 +451,6 @@ static int glink_spi_xprt_read_avail(struct edge_info *einfo)
if (!read_id || !write_id)
return 0;
- if (unlikely(!einfo->rx_fifo_start))
- einfo->rx_fifo_start = read_id;
-
if (read_id <= write_id)
read_avail = write_id - read_id;
else
@@ -471,15 +477,7 @@ static int glink_spi_xprt_rx_cmd(struct edge_info *einfo, void *dst,
uint32_t offset = 0;
int ret;
- ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_read_reg_addr,
- &read_id);
- if (ret < 0) {
- pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n",
- __func__, ret, einfo->xprt_cfg.edge,
- einfo->rx_fifo_read_reg_addr);
- return ret;
- }
-
+ read_id = einfo->rx_fifo_read;
do {
if ((read_id + size_to_read) >=
(einfo->rx_fifo_start + einfo->fifo_size))
@@ -504,6 +502,9 @@ static int glink_spi_xprt_rx_cmd(struct edge_info *einfo, void *dst,
pr_err("%s: Error %d writing %s rx_fifo_read_reg_addr %d\n",
__func__, ret, einfo->xprt_cfg.edge,
einfo->rx_fifo_read_reg_addr);
+ else
+ einfo->rx_fifo_read = read_id;
+
return ret;
}
@@ -526,15 +527,7 @@ static int glink_spi_xprt_tx_cmd_safe(struct edge_info *einfo, void *src,
uint32_t offset = 0;
int ret;
- ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_write_reg_addr,
- &write_id);
- if (ret < 0) {
- pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n",
- __func__, ret, einfo->xprt_cfg.edge,
- einfo->tx_fifo_write_reg_addr);
- return ret;
- }
-
+ write_id = einfo->tx_fifo_write;
do {
if ((write_id + size_to_write) >=
(einfo->tx_fifo_start + einfo->fifo_size))
@@ -559,6 +552,9 @@ static int glink_spi_xprt_tx_cmd_safe(struct edge_info *einfo, void *src,
pr_err("%s: Error %d writing %s tx_fifo_write_reg_addr %d\n",
__func__, ret, einfo->xprt_cfg.edge,
einfo->tx_fifo_write_reg_addr);
+ else
+ einfo->tx_fifo_write = write_id;
+
return ret;
}
@@ -1236,6 +1232,8 @@ static int ssr(struct glink_transport_if *if_ptr)
einfo->tx_blocked_signal_sent = false;
einfo->tx_fifo_start = 0;
einfo->rx_fifo_start = 0;
+ einfo->tx_fifo_write = 0;
+ einfo->rx_fifo_read = 0;
einfo->fifo_size = DEFAULT_FIFO_SIZE;
einfo->xprt_if.glink_core_if_ptr->link_down(&einfo->xprt_if);
diff --git a/drivers/soc/qcom/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(&current->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(&current->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, &param, 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, &param, 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, &param, 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, &param, 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 bf815cb68f90..2326487302fd 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -152,6 +152,8 @@ bool ignore_qmi_timeout;
#define ICNSS_QMI_ASSERT() do { } while (0)
#endif
+#define QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED 0x77
+
enum icnss_debug_quirks {
HW_ALWAYS_ON,
HW_DEBUG_ENABLE,
@@ -266,7 +268,6 @@ struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
struct icnss_event_pd_service_down_data {
bool crashed;
bool fw_rejuvenate;
- bool wdog_bite;
};
struct icnss_driver_event {
@@ -291,9 +292,9 @@ enum icnss_driver_state {
ICNSS_PD_RESTART,
ICNSS_MSA0_ASSIGNED,
ICNSS_WLFW_EXISTS,
- ICNSS_WDOG_BITE,
ICNSS_SHUTDOWN_DONE,
ICNSS_HOST_TRIGGERED_PDR,
+ ICNSS_FW_DOWN,
};
struct ce_irq_list {
@@ -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;
@@ -735,7 +736,7 @@ static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
priv->stats.vbatt_resp++;
@@ -1219,7 +1220,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
@@ -1291,7 +1292,7 @@ static int wlfw_msa_ready_send_sync_msg(void)
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
penv->stats.msa_ready_resp++;
@@ -1354,7 +1355,7 @@ static int wlfw_ind_register_send_sync_msg(void)
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
penv->stats.ind_register_resp++;
@@ -1401,7 +1402,9 @@ static int wlfw_cap_send_sync_msg(void)
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
+ if (resp.resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED)
+ icnss_pr_err("RF card Not present");
goto out;
}
@@ -1484,7 +1487,7 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
mode, resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
penv->stats.mode_resp++;
@@ -1534,7 +1537,7 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
penv->stats.cfg_resp++;
@@ -1587,7 +1590,7 @@ static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
fw_log_mode, resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
penv->stats.ini_resp++;
@@ -1647,7 +1650,7 @@ static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
resp->resp.result, resp->resp.error);
- ret = resp->resp.result;
+ ret = -resp->resp.result;
goto out;
}
@@ -1713,7 +1716,7 @@ static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
out:
@@ -1808,7 +1811,7 @@ static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
priv->stats.rejuvenate_ack_resp++;
@@ -1869,7 +1872,7 @@ static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ ret = -resp.resp.result;
goto out;
}
@@ -1948,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,
@@ -1994,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) {
@@ -2149,10 +2159,7 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv)
icnss_pm_relax(priv);
- if (test_bit(ICNSS_WDOG_BITE, &priv->state)) {
- icnss_call_driver_shutdown(priv);
- clear_bit(ICNSS_WDOG_BITE, &priv->state);
- }
+ icnss_call_driver_shutdown(priv);
clear_bit(ICNSS_PD_RESTART, &priv->state);
@@ -2302,8 +2309,7 @@ static int icnss_call_driver_remove(struct icnss_priv *priv)
static int icnss_fw_crashed(struct icnss_priv *priv,
struct icnss_event_pd_service_down_data *event_data)
{
- icnss_pr_dbg("FW crashed, state: 0x%lx, wdog_bite: %d\n",
- priv->state, event_data->wdog_bite);
+ icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
set_bit(ICNSS_PD_RESTART, &priv->state);
clear_bit(ICNSS_FW_READY, &priv->state);
@@ -2313,17 +2319,9 @@ static int icnss_fw_crashed(struct icnss_priv *priv,
if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
- if (event_data->wdog_bite) {
- set_bit(ICNSS_WDOG_BITE, &priv->state);
- goto out;
- }
-
- icnss_call_driver_shutdown(priv);
-
if (event_data->fw_rejuvenate)
wlfw_rejuvenate_ack_send_sync_msg(priv);
-out:
return 0;
}
@@ -2506,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
@@ -2520,9 +2520,6 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
event_data->crashed = notif->crashed;
- if (notif->crashed == CRASH_STATUS_WDOG_BITE)
- event_data->wdog_bite = true;
-
fw_down_data.crashed = !!notif->crashed;
icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
@@ -2612,7 +2609,6 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
switch (*state) {
case ROOT_PD_WDOG_BITE:
- event_data->wdog_bite = true;
priv->stats.recovery.root_pd_crash++;
break;
case ROOT_PD_SHUTDOWN:
@@ -2637,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);
@@ -2645,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;
}
@@ -3832,15 +3831,15 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
case ICNSS_WLFW_EXISTS:
seq_puts(s, "WLAN FW EXISTS");
continue;
- case ICNSS_WDOG_BITE:
- seq_puts(s, "MODEM WDOG BITE");
- continue;
case ICNSS_SHUTDOWN_DONE:
seq_puts(s, "SHUTDOWN DONE");
continue;
case ICNSS_HOST_TRIGGERED_PDR:
seq_puts(s, "HOST TRIGGERED PDR");
continue;
+ case ICNSS_FW_DOWN:
+ seq_puts(s, "FW DOWN");
+ continue;
}
seq_printf(s, "UNKNOWN-%d", i);
diff --git a/drivers/soc/qcom/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/jtagv8-etm.c b/drivers/soc/qcom/jtagv8-etm.c
index 2c15f7896c82..63432e6026e2 100644
--- a/drivers/soc/qcom/jtagv8-etm.c
+++ b/drivers/soc/qcom/jtagv8-etm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1503,6 +1503,7 @@ static int jtag_mm_etm_callback(struct notifier_block *nfb,
void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
+ u64 version = 0;
if (!etm[cpu])
goto out;
@@ -1524,8 +1525,8 @@ static int jtag_mm_etm_callback(struct notifier_block *nfb,
goto out;
}
if (etm_arch_supported(etm[cpu]->arch)) {
- if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) <
- TZ_DBG_ETM_VER)
+ if (!scm_get_feat_version(TZ_DBG_ETM_FEAT_ID, &version)
+ && version < TZ_DBG_ETM_VER)
etm[cpu]->save_restore_enabled = true;
else
pr_info("etm save-restore supported by TZ\n");
@@ -1615,8 +1616,10 @@ static int jtag_mm_etm_probe(struct platform_device *pdev, uint32_t cpu)
mutex_lock(&etmdata->mutex);
if (etmdata->init && !etmdata->enable) {
if (etm_arch_supported(etmdata->arch)) {
- if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) <
- TZ_DBG_ETM_VER)
+ u64 version = 0;
+
+ if (!scm_get_feat_version(TZ_DBG_ETM_FEAT_ID, &version)
+ && (version < TZ_DBG_ETM_VER))
etmdata->save_restore_enabled = true;
else
pr_info("etm save-restore supported by TZ\n");
diff --git a/drivers/soc/qcom/jtagv8.c b/drivers/soc/qcom/jtagv8.c
index 94c391eabaea..f09ccce8f9c3 100644
--- a/drivers/soc/qcom/jtagv8.c
+++ b/drivers/soc/qcom/jtagv8.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -974,7 +974,7 @@ static struct notifier_block jtag_cpu_pm_notifier = {
static int __init msm_jtag_dbg_init(void)
{
int ret;
-
+ u64 version = 0;
if (msm_jtag_fuse_apps_access_disabled())
return -EPERM;
@@ -982,7 +982,8 @@ static int __init msm_jtag_dbg_init(void)
dbg_init_arch_data();
if (dbg_arch_supported(dbg.arch)) {
- if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < TZ_DBG_ETM_VER) {
+ if (!scm_get_feat_version(TZ_DBG_ETM_FEAT_ID, &version) &&
+ version < TZ_DBG_ETM_VER) {
dbg.save_restore_enabled = true;
} else {
pr_info("dbg save-restore supported by TZ\n");
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index ace2bc4f30b6..d77a12626330 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -56,7 +56,9 @@
#endif
#define PIL_NUM_DESC 10
+#define NUM_OF_ENCRYPTED_KEY 3
static void __iomem *pil_info_base;
+static void __iomem *pil_minidump_base;
/**
* proxy_timeout - Override for proxy vote timeouts
@@ -80,6 +82,18 @@ struct pil_mdt {
};
/**
+ * struct boot_minidump_smem_region - Representation of SMEM TOC
+ * @region_name: Name of modem segment to be dumped
+ * @region_base_address: Where segment start from
+ * @region_size: Size of segment to be dumped
+ */
+struct boot_minidump_smem_region {
+ char region_name[16];
+ u64 region_base_address;
+ u64 region_size;
+};
+
+/**
* struct pil_seg - memory map representing one segment
* @next: points to next seg mentor NULL if last segment
* @paddr: physical start address of segment
@@ -133,11 +147,67 @@ struct pil_priv {
phys_addr_t region_end;
void *region;
struct pil_image_info __iomem *info;
+ struct md_ssr_ss_info __iomem *minidump;
+ int minidump_id;
int id;
int unvoted_flag;
size_t region_size;
};
+static int pil_do_minidump(struct pil_desc *desc, void *ramdump_dev)
+{
+ struct boot_minidump_smem_region __iomem *region_info;
+ struct ramdump_segment *ramdump_segs, *s;
+ struct pil_priv *priv = desc->priv;
+ void __iomem *subsys_smem_base;
+ void __iomem *offset;
+ int ss_mdump_seg_cnt;
+ int ret, i;
+
+ memcpy(&offset, &priv->minidump, sizeof(priv->minidump));
+ offset = offset + sizeof(priv->minidump->md_ss_smem_regions_baseptr);
+ /* There are 3 encryption keys which also need to be dumped */
+ ss_mdump_seg_cnt = readb_relaxed(offset) +
+ NUM_OF_ENCRYPTED_KEY;
+
+ subsys_smem_base = ioremap(__raw_readl(priv->minidump),
+ ss_mdump_seg_cnt * sizeof(*region_info));
+ region_info =
+ (struct boot_minidump_smem_region __iomem *)subsys_smem_base;
+ ramdump_segs = kcalloc(ss_mdump_seg_cnt,
+ sizeof(*ramdump_segs), GFP_KERNEL);
+ if (!ramdump_segs)
+ return -ENOMEM;
+
+ if (desc->subsys_vmid > 0)
+ ret = pil_assign_mem_to_linux(desc, priv->region_start,
+ (priv->region_end - priv->region_start));
+
+ s = ramdump_segs;
+ for (i = 0; i < ss_mdump_seg_cnt; i++) {
+ memcpy(&offset, &region_info, sizeof(region_info));
+ memcpy(&s->name, &region_info, sizeof(region_info));
+ offset = offset + sizeof(region_info->region_name);
+ s->address = __raw_readl(offset);
+ offset = offset + sizeof(region_info->region_base_address);
+ s->size = __raw_readl(offset);
+ s++;
+ region_info++;
+ }
+ ret = do_minidump(ramdump_dev, ramdump_segs, ss_mdump_seg_cnt);
+ kfree(ramdump_segs);
+ if (ret)
+ pil_err(desc, "%s: Ramdump collection failed for subsys %s rc:%d\n",
+ __func__, desc->name, ret);
+ writel_relaxed(0, &priv->minidump->md_ss_smem_regions_baseptr);
+ writeb_relaxed(1, &priv->minidump->md_ss_ssr_cause);
+
+ if (desc->subsys_vmid > 0)
+ ret = pil_assign_mem_to_subsys(desc, priv->region_start,
+ (priv->region_end - priv->region_start));
+ return ret;
+}
+
/**
* pil_do_ramdump() - Ramdump an image
* @desc: descriptor from pil_desc_init()
@@ -153,6 +223,9 @@ int pil_do_ramdump(struct pil_desc *desc, void *ramdump_dev)
int count = 0, ret;
struct ramdump_segment *ramdump_segs, *s;
+ if (priv->minidump && (__raw_readl(priv->minidump) > 0))
+ return pil_do_minidump(desc, ramdump_dev);
+
list_for_each_entry(seg, &priv->segs, list)
count++;
@@ -1014,9 +1087,10 @@ bool is_timeout_disabled(void)
int pil_desc_init(struct pil_desc *desc)
{
struct pil_priv *priv;
- int ret;
void __iomem *addr;
+ int ret, ss_imem_offset_mdump;
char buf[sizeof(priv->info->name)];
+ struct device_node *ofnode = desc->dev->of_node;
if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote,
"Invalid proxy voting. Ignoring\n"))
@@ -1039,6 +1113,22 @@ int pil_desc_init(struct pil_desc *desc)
strncpy(buf, desc->name, sizeof(buf));
__iowrite32_copy(priv->info->name, buf, sizeof(buf) / 4);
}
+ if (of_property_read_u32(ofnode, "qcom,minidump-id",
+ &priv->minidump_id))
+ pr_debug("minidump-id not found for %s\n", desc->name);
+ else {
+ ss_imem_offset_mdump =
+ sizeof(struct md_ssr_ss_info) * priv->minidump_id;
+ if (pil_minidump_base) {
+ /* Add 0x4 to get start of struct md_ssr_ss_info base
+ * from struct md_ssr_toc for any subsystem,
+ * struct md_ssr_ss_info is actually the pointer
+ * of ToC in smem for any subsystem.
+ */
+ addr = pil_minidump_base + ss_imem_offset_mdump + 0x4;
+ priv->minidump = (struct md_ssr_ss_info __iomem *)addr;
+ }
+ }
ret = pil_parse_devicetree(desc);
if (ret)
@@ -1148,6 +1238,20 @@ static int __init msm_pil_init(void)
for (i = 0; i < resource_size(&res)/sizeof(u32); i++)
writel_relaxed(0, pil_info_base + (i * sizeof(u32)));
+ np = of_find_compatible_node(NULL, NULL, "qcom,msm-imem-minidump");
+ if (!np) {
+ pr_warn("pil: failed to find qcom,msm-imem-minidump node\n");
+ goto out;
+ } else {
+ pil_minidump_base = of_iomap(np, 0);
+ if (!pil_minidump_base) {
+ pr_err("unable to map pil minidump imem offset\n");
+ goto out;
+ }
+ }
+ for (i = 0; i < sizeof(struct md_ssr_toc)/sizeof(u32); i++)
+ writel_relaxed(0, pil_minidump_base + (i * sizeof(u32)));
+ writel_relaxed(1, pil_minidump_base);
out:
return register_pm_notifier(&pil_pm_notifier);
}
@@ -1158,6 +1262,8 @@ static void __exit msm_pil_exit(void)
unregister_pm_notifier(&pil_pm_notifier);
if (pil_info_base)
iounmap(pil_info_base);
+ if (pil_minidump_base)
+ iounmap(pil_minidump_base);
}
module_exit(msm_pil_exit);
diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h
index 0cd2aeae1edd..908ab78124f7 100644
--- a/drivers/soc/qcom/peripheral-loader.h
+++ b/drivers/soc/qcom/peripheral-loader.h
@@ -74,6 +74,34 @@ struct pil_image_info {
__le32 size;
} __attribute__((__packed__));
+#define MAX_NUM_OF_SS 3
+
+/**
+ * struct md_ssr_ss_info - Info in imem about smem ToC
+ * @md_ss_smem_regions_baseptr: Start physical address of SMEM TOC
+ * @md_ss_num_of_regions: number of segments that need to be dumped
+ * @md_ss_encryption_status: status of encryption of segments
+ * @md_ss_ssr_cause: ssr cause enum
+ */
+struct md_ssr_ss_info {
+ u32 md_ss_smem_regions_baseptr;
+ u8 md_ss_num_of_regions;
+ u8 md_ss_encryption_status;
+ u8 md_ss_ssr_cause;
+ u8 reserved;
+};
+
+/**
+ * struct md_ssr_toc - Wrapper of struct md_ssr_ss_info
+ * @md_ssr_toc_init: flag to indicate to MSS SW about imem init done
+ * @md_ssr_ss: Instance of struct md_ssr_ss_info for a subsystem
+ */
+struct md_ssr_toc /* Shared IMEM ToC struct */
+{
+ u32 md_ssr_toc_init;
+ struct md_ssr_ss_info md_ssr_ss[MAX_NUM_OF_SS];
+};
+
/**
* struct pil_reset_ops - PIL operations
* @init_image: prepare an image for authentication
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index 5fcb0f95733c..4bea034f0bdd 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -78,7 +78,8 @@
#define MSS_MAGIC 0XAABADEAD
/* CX_IPEAK Parameters */
#define CX_IPEAK_MSS BIT(5)
-
+/* Timeout value for MBA boot when minidump is enabled */
+#define MBA_ENCRYPTION_TIMEOUT 3000
enum scm_cmd {
PAS_MEM_SETUP_CMD = 2,
};
@@ -244,7 +245,12 @@ static int pil_msa_wait_for_mba_ready(struct q6v5_data *drv)
struct device *dev = drv->desc.dev;
int ret;
u32 status;
- u64 val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000;
+ u64 val;
+
+ if (of_property_read_bool(dev->of_node, "qcom,minidump-id"))
+ pbl_mba_boot_timeout_ms = MBA_ENCRYPTION_TIMEOUT;
+
+ val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000;
/* Wait for PBL completion. */
ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c
index d14e82415c5a..7a62c2e36da8 100644
--- a/drivers/soc/qcom/qbt1000.c
+++ b/drivers/soc/qcom/qbt1000.c
@@ -12,6 +12,7 @@
#define pr_fmt(fmt) "qbt1000:%s: " fmt, __func__
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
@@ -105,10 +106,14 @@ struct qbt1000_drvdata {
*/
struct fw_ipc_cmd {
uint32_t status;
+ uint32_t numMsgs;
+ uint8_t msg_data[FW_MAX_IPC_MSG_DATA_SIZE];
+};
+
+struct fw_ipc_header {
uint32_t msg_type;
uint32_t msg_len;
uint32_t resp_needed;
- uint8_t msg_data[FW_MAX_IPC_MSG_DATA_SIZE];
};
/*
@@ -352,7 +357,14 @@ static int qbt1000_open(struct inode *inode, struct file *file)
*/
static int qbt1000_release(struct inode *inode, struct file *file)
{
- struct qbt1000_drvdata *drvdata = file->private_data;
+ struct qbt1000_drvdata *drvdata;
+
+ if (!file->private_data) {
+ pr_err("Null pointer passed in file->private_data");
+ return -EINVAL;
+ }
+
+ drvdata = file->private_data;
atomic_inc(&drvdata->available);
return 0;
@@ -374,6 +386,11 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg)
void __user *priv_arg = (void __user *)arg;
struct qbt1000_drvdata *drvdata;
+ if (!file->private_data) {
+ pr_err("Null pointer passed in file->private_data");
+ return -EINVAL;
+ }
+
drvdata = file->private_data;
if (IS_ERR(priv_arg)) {
@@ -789,6 +806,8 @@ static int qbt1000_create_input_device(struct qbt1000_drvdata *drvdata)
BIT_MASK(KEY_HOMEPAGE);
drvdata->in_dev->keybit[BIT_WORD(KEY_CAMERA)] |=
BIT_MASK(KEY_CAMERA);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_VOLUMEDOWN)] |=
+ BIT_MASK(KEY_VOLUMEDOWN);
drvdata->in_dev->keybit[BIT_WORD(KEY_POWER)] |=
BIT_MASK(KEY_POWER);
@@ -917,11 +936,14 @@ static irqreturn_t qbt1000_gpio_isr(int irq, void *dev_id)
*/
static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
{
+ uint8_t *msg_buffer;
struct fw_ipc_cmd *rx_cmd;
- int i;
+ struct fw_ipc_header *header;
+ int i, j;
uint32_t rxipc = FP_APP_CMD_RX_IPC;
struct qbt1000_drvdata *drvdata = (struct qbt1000_drvdata *)dev_id;
int rc = 0;
+ uint32_t retry_count = 10;
pm_stay_awake(drvdata->dev);
@@ -933,18 +955,25 @@ static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
goto end;
}
- pr_debug("firmware interrupt received (irq %d)\n", irq);
-
if (!drvdata->fp_app_handle)
goto end;
- /*
- * send the TZ command to fetch the message from firmware
- * TZ will process the message if it can
- */
- rc = send_tz_cmd(drvdata, drvdata->fp_app_handle, 0,
- &rxipc, sizeof(rxipc),
- (void *)&rx_cmd, sizeof(*rx_cmd));
+ while (retry_count > 0) {
+ /*
+ * send the TZ command to fetch the message from firmware
+ * TZ will process the message if it can
+ */
+ rc = send_tz_cmd(drvdata, drvdata->fp_app_handle, 0,
+ &rxipc, sizeof(rxipc),
+ (void *)&rx_cmd, sizeof(*rx_cmd));
+ if (rc < 0) {
+ msleep(50); /* sleep for 50ms before retry */
+ retry_count -= 1;
+ continue;
+ } else {
+ break;
+ }
+ }
if (rc < 0) {
pr_err("failure sending tz cmd %d\n", rxipc);
@@ -956,29 +985,35 @@ static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
goto end;
}
- /*
- * given the IPC message type, search for a corresponding event for the
- * driver client. If found, add to the events FIFO
- */
- for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) {
- if (g_msg_to_event[i].msg_type == rx_cmd->msg_type) {
- enum qbt1000_fw_event ev = g_msg_to_event[i].fw_event;
- struct fw_event_desc fw_ev_desc;
-
- mutex_lock(&drvdata->fw_events_mutex);
- pr_debug("fw events: add %d\n", (int) ev);
- fw_ev_desc.ev = ev;
-
- if (!kfifo_put(&drvdata->fw_events, fw_ev_desc))
- pr_err("fw events: fifo full, drop event %d\n",
- (int) ev);
-
- mutex_unlock(&drvdata->fw_events_mutex);
- wake_up_interruptible(&drvdata->read_wait_queue);
- break;
+ msg_buffer = rx_cmd->msg_data;
+
+ for (j = 0; j < rx_cmd->numMsgs; j++) {
+ header = (struct fw_ipc_header *) msg_buffer;
+ /*
+ * given the IPC message type, search for a corresponding event
+ * for the driver client. If found, add to the events FIFO
+ */
+ for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) {
+ if (g_msg_to_event[i].msg_type == header->msg_type) {
+ enum qbt1000_fw_event ev =
+ g_msg_to_event[i].fw_event;
+ struct fw_event_desc fw_ev_desc;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+ pr_debug("fw events: add %d\n", (int) ev);
+ fw_ev_desc.ev = ev;
+
+ if (!kfifo_put(&drvdata->fw_events, fw_ev_desc))
+ pr_err("fw events: fifo full, drop event %d\n",
+ (int) ev);
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+ break;
+ }
}
+ msg_buffer += sizeof(*header) + header->msg_len;
}
-
+ wake_up_interruptible(&drvdata->read_wait_queue);
end:
mutex_unlock(&drvdata->mutex);
pm_relax(drvdata->dev);
diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c
index eca992ec17e4..3791169ec0ac 100644
--- a/drivers/soc/qcom/qdsp6v2/apr.c
+++ b/drivers/soc/qcom/qdsp6v2/apr.c
@@ -820,6 +820,7 @@ static void dispatch_event(unsigned long code, uint16_t proc)
uint16_t clnt;
int i, j;
+ memset(&data, 0, sizeof(data));
data.opcode = RESET_EVENTS;
data.reset_event = code;
diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c
index 83e3775ed533..9e61ff1ebfcc 100644
--- a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c
+++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,7 @@
#include <linux/export.h>
#include <linux/qcom_iommu.h>
#include <asm/dma-iommu.h>
+#include <soc/qcom/secure_buffer.h>
#define MSM_AUDIO_ION_PROBED (1 << 0)
@@ -178,6 +179,123 @@ err:
}
EXPORT_SYMBOL(msm_audio_ion_alloc);
+static int msm_audio_hyp_assign(ion_phys_addr_t *paddr, size_t *pa_len,
+ u8 assign_type)
+{
+ int srcVM[1] = {VMID_HLOS};
+ int destVM[1] = {VMID_CP_ADSP_SHARED};
+ int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+ int ret = 0;
+
+ switch (assign_type) {
+ case HLOS_TO_ADSP:
+ srcVM[0] = VMID_HLOS;
+ destVM[0] = VMID_CP_ADSP_SHARED;
+ break;
+ case ADSP_TO_HLOS:
+ srcVM[0] = VMID_CP_ADSP_SHARED;
+ destVM[0] = VMID_HLOS;
+ break;
+ default:
+ pr_err("%s: Invalid assign type = %d\n", __func__, assign_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = hyp_assign_phys(*paddr, *pa_len, srcVM, 1, destVM, destVMperm, 1);
+ if (ret)
+ pr_err("%s: hyp_assign_phys failed for type %d, rc = %d\n",
+ __func__, assign_type, ret);
+done:
+ return ret;
+}
+
+int msm_audio_ion_phys_free(struct ion_client *client,
+ struct ion_handle *handle,
+ ion_phys_addr_t *paddr,
+ size_t *pa_len, u8 assign_type)
+{
+ int ret;
+
+ if (!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
+ pr_debug("%s:probe is not done, deferred\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ if (!client || !handle || !paddr || !pa_len) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = ion_phys(client, handle, paddr, pa_len);
+ if (ret) {
+ pr_err("%s: could not get physical address for handle, ret = %d\n",
+ __func__, ret);
+ goto err_ion_handle;
+ }
+
+ ret = msm_audio_hyp_assign(paddr, pa_len, assign_type);
+
+err_ion_handle:
+ ion_free(client, handle);
+ ion_client_destroy(client);
+
+ return ret;
+}
+
+int msm_audio_ion_phys_assign(const char *name, struct ion_client **client,
+ struct ion_handle **handle, int fd,
+ ion_phys_addr_t *paddr,
+ size_t *pa_len, u8 assign_type)
+{
+ int ret;
+
+ if (!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
+ pr_debug("%s:probe is not done, deferred\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ if (!name || !client || !handle || !paddr || !pa_len) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ *client = msm_audio_ion_client_create(name);
+ if (IS_ERR_OR_NULL((void *)(*client))) {
+ pr_err("%s: ION create client failed\n", __func__);
+ return -EINVAL;
+ }
+
+ *handle = ion_import_dma_buf(*client, fd);
+ if (IS_ERR_OR_NULL((void *) (*handle))) {
+ pr_err("%s: ion import dma buffer failed\n",
+ __func__);
+ ret = -EINVAL;
+ goto err_destroy_client;
+ }
+
+ ret = ion_phys(*client, *handle, paddr, pa_len);
+ if (ret) {
+ pr_err("%s: could not get physical address for handle, ret = %d\n",
+ __func__, ret);
+ goto err_ion_handle;
+ }
+
+ ret = msm_audio_hyp_assign(paddr, pa_len, assign_type);
+
+ return ret;
+
+err_ion_handle:
+ ion_free(*client, *handle);
+
+err_destroy_client:
+ ion_client_destroy(*client);
+ *client = NULL;
+ *handle = NULL;
+
+ return ret;
+}
+
int msm_audio_ion_import(const char *name, struct ion_client **client,
struct ion_handle **handle, int fd,
unsigned long *ionflag, size_t bufsz,
diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c
index d86f8671705a..38cc86963181 100644
--- a/drivers/soc/qcom/qpnp-haptic.c
+++ b/drivers/soc/qcom/qpnp-haptic.c
@@ -2233,13 +2233,23 @@ static void qpnp_hap_td_enable(struct timed_output_dev *dev, int time_ms)
timed_dev);
int rc;
- if (time_ms <= 0)
+ if (time_ms < 0)
return;
+ mutex_lock(&hap->lock);
+
+ if (time_ms == 0) {
+ /* disable haptics */
+ hrtimer_cancel(&hap->hap_timer);
+ hap->state = 0;
+ schedule_work(&hap->work);
+ mutex_unlock(&hap->lock);
+ return;
+ }
+
if (time_ms < 10)
time_ms = 10;
- mutex_lock(&hap->lock);
if (is_sw_lra_auto_resonance_control(hap))
hrtimer_cancel(&hap->auto_res_err_poll_timer);
diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c
index c712ed392b0b..c8353dc8a43a 100644
--- a/drivers/soc/qcom/ramdump.c
+++ b/drivers/soc/qcom/ramdump.c
@@ -29,6 +29,8 @@
#include <linux/of.h>
#define RAMDUMP_WAIT_MSECS 120000
+#define MAX_STRTBL_SIZE 512
+#define MAX_NAME_LENGTH 16
struct ramdump_device {
char name[256];
@@ -391,12 +393,143 @@ static int _do_ramdump(void *handle, struct ramdump_segment *segments,
}
+static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr)
+{
+ return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff);
+}
+
+static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx)
+{
+ return &elf_sheader(hdr)[idx];
+}
+
+static inline char *elf_str_table(struct elfhdr *hdr)
+{
+ if (hdr->e_shstrndx == SHN_UNDEF)
+ return NULL;
+ return (char *)hdr + elf_section(hdr, hdr->e_shstrndx)->sh_offset;
+}
+
+static inline unsigned int set_section_name(const char *name,
+ struct elfhdr *ehdr)
+{
+ char *strtab = elf_str_table(ehdr);
+ static int strtable_idx = 1;
+ int idx, ret = 0;
+
+ idx = strtable_idx;
+ if ((strtab == NULL) || (name == NULL))
+ return 0;
+
+ ret = idx;
+ idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH);
+ strtable_idx = idx + 1;
+
+ return ret;
+}
+
+static int _do_minidump(void *handle, struct ramdump_segment *segments,
+ int nsegments)
+{
+ int ret, i;
+ struct ramdump_device *rd_dev = (struct ramdump_device *)handle;
+ struct elfhdr *ehdr;
+ struct elf_shdr *shdr;
+ unsigned long offset, strtbl_off;
+
+ if (!rd_dev->consumer_present) {
+ pr_err("Ramdump(%s): No consumers. Aborting..\n", rd_dev->name);
+ return -EPIPE;
+ }
+
+ rd_dev->segments = segments;
+ rd_dev->nsegments = nsegments;
+
+ rd_dev->elfcore_size = sizeof(*ehdr) +
+ (sizeof(*shdr) * (nsegments + 2)) + MAX_STRTBL_SIZE;
+ ehdr = kzalloc(rd_dev->elfcore_size, GFP_KERNEL);
+ rd_dev->elfcore_buf = (char *)ehdr;
+ if (!rd_dev->elfcore_buf)
+ return -ENOMEM;
+
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_CLASS] = ELF_CLASS;
+ ehdr->e_ident[EI_DATA] = ELF_DATA;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELF_OSABI;
+ ehdr->e_type = ET_CORE;
+ ehdr->e_machine = ELF_ARCH;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_ehsize = sizeof(*ehdr);
+ ehdr->e_shoff = sizeof(*ehdr);
+ ehdr->e_shentsize = sizeof(*shdr);
+ ehdr->e_shstrndx = 1;
+
+
+ offset = rd_dev->elfcore_size;
+ shdr = (struct elf_shdr *)(ehdr + 1);
+ strtbl_off = sizeof(*ehdr) + sizeof(*shdr) * (nsegments + 2);
+ shdr++;
+ shdr->sh_type = SHT_STRTAB;
+ shdr->sh_offset = (elf_addr_t)strtbl_off;
+ shdr->sh_size = MAX_STRTBL_SIZE;
+ shdr->sh_entsize = 0;
+ shdr->sh_flags = 0;
+ shdr->sh_name = set_section_name("STR_TBL", ehdr);
+ shdr++;
+
+ for (i = 0; i < nsegments; i++, shdr++) {
+ /* Update elf header */
+ shdr->sh_type = SHT_PROGBITS;
+ shdr->sh_name = set_section_name(segments[i].name, ehdr);
+ shdr->sh_addr = (elf_addr_t)segments[i].address;
+ shdr->sh_size = segments[i].size;
+ shdr->sh_flags = SHF_WRITE;
+ shdr->sh_offset = offset;
+ shdr->sh_entsize = 0;
+ offset += shdr->sh_size;
+ }
+ ehdr->e_shnum = nsegments + 2;
+
+ rd_dev->data_ready = 1;
+ rd_dev->ramdump_status = -1;
+
+ reinit_completion(&rd_dev->ramdump_complete);
+
+ /* Tell userspace that the data is ready */
+ wake_up(&rd_dev->dump_wait_q);
+
+ /* Wait (with a timeout) to let the ramdump complete */
+ ret = wait_for_completion_timeout(&rd_dev->ramdump_complete,
+ msecs_to_jiffies(RAMDUMP_WAIT_MSECS));
+
+ if (!ret) {
+ pr_err("Ramdump(%s): Timed out waiting for userspace.\n",
+ rd_dev->name);
+ ret = -EPIPE;
+ } else {
+ ret = (rd_dev->ramdump_status == 0) ? 0 : -EPIPE;
+ }
+
+ rd_dev->data_ready = 0;
+ rd_dev->elfcore_size = 0;
+ kfree(rd_dev->elfcore_buf);
+ rd_dev->elfcore_buf = NULL;
+ return ret;
+}
+
int do_ramdump(void *handle, struct ramdump_segment *segments, int nsegments)
{
return _do_ramdump(handle, segments, nsegments, false);
}
EXPORT_SYMBOL(do_ramdump);
+int do_minidump(void *handle, struct ramdump_segment *segments, int nsegments)
+{
+ return _do_minidump(handle, segments, nsegments);
+}
+EXPORT_SYMBOL(do_minidump);
+
int
do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments)
{
diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c
index 31de6e5c173c..43e2e4d17648 100644
--- a/drivers/soc/qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -397,18 +397,22 @@ static int __scm_call_armv8_64(u64 x0, u64 x1, u64 x2, u64 x3, u64 x4, u64 x5,
__asmeq("%1", R1_STR)
__asmeq("%2", R2_STR)
__asmeq("%3", R3_STR)
- __asmeq("%4", R0_STR)
- __asmeq("%5", R1_STR)
- __asmeq("%6", R2_STR)
- __asmeq("%7", R3_STR)
- __asmeq("%8", R4_STR)
- __asmeq("%9", R5_STR)
- __asmeq("%10", R6_STR)
+ __asmeq("%4", R4_STR)
+ __asmeq("%5", R5_STR)
+ __asmeq("%6", R6_STR)
+ __asmeq("%7", R0_STR)
+ __asmeq("%8", R1_STR)
+ __asmeq("%9", R2_STR)
+ __asmeq("%10", R3_STR)
+ __asmeq("%11", R4_STR)
+ __asmeq("%12", R5_STR)
+ __asmeq("%13", R6_STR)
#ifdef REQUIRES_SEC
".arch_extension sec\n"
#endif
"smc #0\n"
- : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3)
+ : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3),
+ "=r" (r4), "=r" (r5), "=r" (r6)
: "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4),
"r" (r5), "r" (r6)
: "x7", "x8", "x9", "x10", "x11", "x12", "x13",
@@ -442,18 +446,22 @@ static int __scm_call_armv8_32(u32 w0, u32 w1, u32 w2, u32 w3, u32 w4, u32 w5,
__asmeq("%1", R1_STR)
__asmeq("%2", R2_STR)
__asmeq("%3", R3_STR)
- __asmeq("%4", R0_STR)
- __asmeq("%5", R1_STR)
- __asmeq("%6", R2_STR)
- __asmeq("%7", R3_STR)
- __asmeq("%8", R4_STR)
- __asmeq("%9", R5_STR)
- __asmeq("%10", R6_STR)
+ __asmeq("%4", R4_STR)
+ __asmeq("%5", R5_STR)
+ __asmeq("%6", R6_STR)
+ __asmeq("%7", R0_STR)
+ __asmeq("%8", R1_STR)
+ __asmeq("%9", R2_STR)
+ __asmeq("%10", R3_STR)
+ __asmeq("%11", R4_STR)
+ __asmeq("%12", R5_STR)
+ __asmeq("%13", R6_STR)
#ifdef REQUIRES_SEC
".arch_extension sec\n"
#endif
"smc #0\n"
- : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3)
+ : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3),
+ "=r" (r4), "=r" (r5), "=r" (r6)
: "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4),
"r" (r5), "r" (r6)
: "x7", "x8", "x9", "x10", "x11", "x12", "x13",
@@ -490,18 +498,22 @@ static int __scm_call_armv8_32(u32 w0, u32 w1, u32 w2, u32 w3, u32 w4, u32 w5,
__asmeq("%1", R1_STR)
__asmeq("%2", R2_STR)
__asmeq("%3", R3_STR)
- __asmeq("%4", R0_STR)
- __asmeq("%5", R1_STR)
- __asmeq("%6", R2_STR)
- __asmeq("%7", R3_STR)
- __asmeq("%8", R4_STR)
- __asmeq("%9", R5_STR)
- __asmeq("%10", R6_STR)
+ __asmeq("%4", R4_STR)
+ __asmeq("%5", R5_STR)
+ __asmeq("%6", R6_STR)
+ __asmeq("%7", R0_STR)
+ __asmeq("%8", R1_STR)
+ __asmeq("%9", R2_STR)
+ __asmeq("%10", R3_STR)
+ __asmeq("%11", R4_STR)
+ __asmeq("%12", R5_STR)
+ __asmeq("%13", R6_STR)
#ifdef REQUIRES_SEC
".arch_extension sec\n"
#endif
"smc #0\n"
- : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3)
+ : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3),
+ "=r" (r4), "=r" (r5), "=r" (r6)
: "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4),
"r" (r5), "r" (r6));
@@ -1117,54 +1129,55 @@ int scm_is_call_available(u32 svc_id, u32 cmd_id)
ret = scm_call(SCM_SVC_INFO, IS_CALL_AVAIL_CMD, &svc_cmd,
sizeof(svc_cmd), &ret_val, sizeof(ret_val));
- if (ret)
- return ret;
+ if (!ret && ret_val)
+ return 1;
+ else
+ return 0;
- return ret_val;
}
desc.arginfo = SCM_ARGS(1);
desc.args[0] = SCM_SIP_FNID(svc_id, cmd_id);
+ desc.ret[0] = 0;
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, IS_CALL_AVAIL_CMD), &desc);
- if (ret)
- return ret;
+ if (!ret && desc.ret[0])
+ return 1;
+ else
+ return 0;
- return desc.ret[0];
}
EXPORT_SYMBOL(scm_is_call_available);
#define GET_FEAT_VERSION_CMD 3
-int scm_get_feat_version(u32 feat)
+int scm_get_feat_version(u32 feat, u64 *scm_ret)
{
struct scm_desc desc = {0};
int ret;
if (!is_scm_armv8()) {
if (scm_is_call_available(SCM_SVC_INFO, GET_FEAT_VERSION_CMD)) {
- u32 version;
- if (!scm_call(SCM_SVC_INFO, GET_FEAT_VERSION_CMD, &feat,
- sizeof(feat), &version, sizeof(version)))
- return version;
+ ret = scm_call(SCM_SVC_INFO, GET_FEAT_VERSION_CMD,
+ &feat, sizeof(feat), scm_ret, sizeof(*scm_ret));
+ return ret;
}
- return 0;
}
ret = scm_is_call_available(SCM_SVC_INFO, GET_FEAT_VERSION_CMD);
if (ret <= 0)
- return 0;
+ return -EAGAIN;
desc.args[0] = feat;
desc.arginfo = SCM_ARGS(1);
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, GET_FEAT_VERSION_CMD),
&desc);
- if (!ret)
- return desc.ret[0];
- return 0;
+ *scm_ret = desc.ret[0];
+
+ return ret;
}
EXPORT_SYMBOL(scm_get_feat_version);
#define RESTORE_SEC_CFG 2
-int scm_restore_sec_cfg(u32 device_id, u32 spare, int *scm_ret)
+int scm_restore_sec_cfg(u32 device_id, u32 spare, u64 *scm_ret)
{
struct scm_desc desc = {0};
int ret;
diff --git a/drivers/soc/qcom/scm_qcpe.c b/drivers/soc/qcom/scm_qcpe.c
index 54a978157bda..3f2b05a0ec9e 100644
--- a/drivers/soc/qcom/scm_qcpe.c
+++ b/drivers/soc/qcom/scm_qcpe.c
@@ -27,7 +27,7 @@
#define CREATE_TRACE_POINTS
#include <trace/events/scm.h>
-#include <uapi/linux/habmm.h>
+#include <linux/habmm.h>
#define SCM_ENOMEM (-5)
#define SCM_EOPNOTSUPP (-4)
@@ -218,8 +218,8 @@ static int scm_call_qcpe(u32 fn_id, struct scm_desc *desc)
if (!opened) {
ret = habmm_socket_open(&handle, MM_QCPE_VM1, 0, 0);
- if (ret != HAB_OK) {
- pr_err("scm_call2: habmm_socket_open failed with ret = %d",
+ if (ret) {
+ pr_err("scm_call_qcpe: habmm_socket_open failed with ret = %d",
ret);
return ret;
}
@@ -235,23 +235,30 @@ static int scm_call_qcpe(u32 fn_id, struct scm_desc *desc)
smc_params.sid = 0;
ret = habmm_socket_send(handle, &smc_params, sizeof(smc_params), 0);
- if (ret != HAB_OK)
+ if (ret)
return ret;
size_bytes = sizeof(smc_params);
+ memset(&smc_params, 0x0, sizeof(smc_params));
ret = habmm_socket_recv(handle, &smc_params, &size_bytes, 0, 0);
- if (ret != HAB_OK)
+ if (ret)
return ret;
+ if (size_bytes != sizeof(smc_params)) {
+ pr_err("scm_call_qcpe: expected size: %lu, actual=%u\n",
+ sizeof(smc_params), size_bytes);
+ return SCM_ERROR;
+ }
+
desc->ret[0] = smc_params.x1;
desc->ret[1] = smc_params.x2;
desc->ret[2] = smc_params.x3;
- pr_info("scm_call_qcpe: OUT: 0x%llx, 0x%llx, 0x%llx",
- desc->ret[0], desc->ret[1], desc->ret[2]);
+ pr_info("scm_call_qcpe: OUT: 0x%llx, 0x%llx, 0x%llx, 0x%llx",
+ smc_params.x0, desc->ret[0], desc->ret[1], desc->ret[2]);
- return 0;
+ return smc_params.x0;
}
static u32 smc(u32 cmd_addr)
@@ -1020,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/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c
index 4307937d9f6d..e7a00cdb5b03 100644
--- a/drivers/soc/qcom/secure_buffer.c
+++ b/drivers/soc/qcom/secure_buffer.c
@@ -424,13 +424,14 @@ const char *msm_secure_vmid_to_string(int secure_vmid)
bool msm_secure_v2_is_supported(void)
{
- int version = scm_get_feat_version(FEATURE_ID_CP);
+ u64 version;
+ int ret = scm_get_feat_version(FEATURE_ID_CP, &version);
/*
* if the version is < 1.1.0 then dynamic buffer allocation is
* not supported
*/
- return version >= MAKE_CP_VERSION(1, 1, 0);
+ return (ret == 0) && (version >= MAKE_CP_VERSION(1, 1, 0));
}
static int __init alloc_secure_shared_memory(void)
diff --git a/drivers/soc/qcom/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-dw.c b/drivers/spi/spi-dw.c
index 882cd6618cd5..87a0e47eeae6 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -107,7 +107,10 @@ static const struct file_operations dw_spi_regs_ops = {
static int dw_spi_debugfs_init(struct dw_spi *dws)
{
- dws->debugfs = debugfs_create_dir("dw_spi", NULL);
+ char name[128];
+
+ snprintf(name, 128, "dw_spi-%s", dev_name(&dws->master->dev));
+ dws->debugfs = debugfs_create_dir(name, NULL);
if (!dws->debugfs)
return -ENOMEM;
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 4499dd35f2dd..da58f19dd6e6 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1827,14 +1827,16 @@ static int msm_spi_setup(struct spi_device *spi)
mb();
if (dd->pdata->is_shared)
put_local_resources(dd);
- /* Counter-part of system-resume when runtime-pm is not enabled. */
- if (!pm_runtime_enabled(dd->dev))
- msm_spi_pm_suspend_runtime(dd->dev);
no_resources:
mutex_unlock(&dd->core_lock);
- pm_runtime_mark_last_busy(dd->dev);
- pm_runtime_put_autosuspend(dd->dev);
+ /* Counter-part of system-resume when runtime-pm is not enabled. */
+ if (!pm_runtime_enabled(dd->dev)) {
+ msm_spi_pm_suspend_runtime(dd->dev);
+ } else {
+ pm_runtime_mark_last_busy(dd->dev);
+ pm_runtime_put_autosuspend(dd->dev);
+ }
err_setup_exit:
return rc;
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 9f161b2631f2..e5ca8f150617 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -696,6 +696,8 @@ static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{ .compatible = "lineartechnology,ltc2488" },
{ .compatible = "qcom,spi-msm-codec-slave", },
+ { .compatible = "nxp,mpc57xx", },
+ { .compatible = "infineon,sli97", },
{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
index b43e5656598a..a62ae9061f27 100644
--- a/drivers/spmi/spmi.c
+++ b/drivers/spmi/spmi.c
@@ -364,11 +364,23 @@ static int spmi_drv_remove(struct device *dev)
return 0;
}
+static int spmi_drv_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ int ret;
+
+ ret = of_device_uevent_modalias(dev, env);
+ if (ret != -ENODEV)
+ return ret;
+
+ return 0;
+}
+
static struct bus_type spmi_bus_type = {
.name = "spmi",
.match = spmi_device_match,
.probe = spmi_drv_probe,
.remove = spmi_drv_remove,
+ .uevent = spmi_drv_uevent,
};
/**
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index 07fc21797f0f..067cd58375a4 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -264,7 +264,7 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
mutex_lock(&dev->buffer_lock);
ion_buffer_add(dev, buffer);
mutex_unlock(&dev->buffer_lock);
- atomic_add(len, &heap->total_allocated);
+ atomic_long_add(len, &heap->total_allocated);
return buffer;
err:
@@ -282,7 +282,7 @@ void ion_buffer_destroy(struct ion_buffer *buffer)
buffer->heap->ops->unmap_kernel(buffer->heap, buffer);
buffer->heap->ops->unmap_dma(buffer->heap, buffer);
- atomic_sub(buffer->size, &buffer->heap->total_allocated);
+ atomic_long_sub(buffer->size, &buffer->heap->total_allocated);
buffer->heap->ops->free(buffer);
vfree(buffer->pages);
kfree(buffer);
@@ -320,7 +320,7 @@ static void ion_buffer_add_to_handle(struct ion_buffer *buffer)
{
mutex_lock(&buffer->lock);
if (buffer->handle_count == 0)
- atomic_add(buffer->size, &buffer->heap->total_handles);
+ atomic_long_add(buffer->size, &buffer->heap->total_handles);
buffer->handle_count++;
mutex_unlock(&buffer->lock);
@@ -346,7 +346,7 @@ static void ion_buffer_remove_from_handle(struct ion_buffer *buffer)
task = current->group_leader;
get_task_comm(buffer->task_comm, task);
buffer->pid = task_pid_nr(task);
- atomic_sub(buffer->size, &buffer->heap->total_handles);
+ atomic_long_sub(buffer->size, &buffer->heap->total_handles);
}
mutex_unlock(&buffer->lock);
}
@@ -1951,10 +1951,10 @@ void show_ion_usage(struct ion_device *dev)
"Total orphaned size");
pr_info("---------------------------------\n");
plist_for_each_entry(heap, &dev->heaps, node) {
- pr_info("%16.s 0x%16.x 0x%16.x\n",
- heap->name, atomic_read(&heap->total_allocated),
- atomic_read(&heap->total_allocated) -
- atomic_read(&heap->total_handles));
+ pr_info("%16.s 0x%16.lx 0x%16.lx\n",
+ heap->name, atomic_long_read(&heap->total_allocated),
+ atomic_long_read(&heap->total_allocated) -
+ atomic_long_read(&heap->total_handles));
if (heap->debug_show)
heap->debug_show(heap, NULL, 0);
diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c
index dfb6d2f77af1..d932db4f9810 100644
--- a/drivers/staging/android/ion/ion_cma_heap.c
+++ b/drivers/staging/android/ion/ion_cma_heap.c
@@ -237,10 +237,12 @@ void ion_cma_heap_destroy(struct ion_heap *heap)
static void ion_secure_cma_free(struct ion_buffer *buffer)
{
- int ret = 0;
+ int i, ret = 0;
int source_vm;
int dest_vmid;
int dest_perms;
+ struct sg_table *sgt;
+ struct scatterlist *sg;
struct ion_cma_buffer_info *info = buffer->priv_virt;
source_vm = get_secure_vmid(buffer->flags);
@@ -251,14 +253,17 @@ static void ion_secure_cma_free(struct ion_buffer *buffer)
dest_vmid = VMID_HLOS;
dest_perms = PERM_READ | PERM_WRITE | PERM_EXEC;
- ret = hyp_assign_table(info->table, &source_vm, 1,
- &dest_vmid, &dest_perms, 1);
+ sgt = info->table;
+ ret = hyp_assign_table(sgt, &source_vm, 1, &dest_vmid, &dest_perms, 1);
if (ret) {
pr_err("%s: Not freeing memory since assign failed\n",
__func__);
return;
}
+ for_each_sg(sgt->sgl, sg, sgt->nents, i)
+ ClearPagePrivate(sg_page(sg));
+
ion_cma_free(buffer);
}
@@ -266,11 +271,13 @@ static int ion_secure_cma_allocate(struct ion_heap *heap,
struct ion_buffer *buffer, unsigned long len,
unsigned long align, unsigned long flags)
{
- int ret = 0;
+ int i, ret = 0;
int source_vm;
int dest_vm;
int dest_perms;
struct ion_cma_buffer_info *info;
+ struct sg_table *sgt;
+ struct scatterlist *sg;
source_vm = VMID_HLOS;
dest_vm = get_secure_vmid(flags);
@@ -292,12 +299,17 @@ static int ion_secure_cma_allocate(struct ion_heap *heap,
}
info = buffer->priv_virt;
- ret = hyp_assign_table(info->table, &source_vm, 1,
- &dest_vm, &dest_perms, 1);
+ sgt = info->table;
+ ret = hyp_assign_table(sgt, &source_vm, 1, &dest_vm, &dest_perms, 1);
if (ret) {
pr_err("%s: Assign call failed\n", __func__);
goto err;
}
+
+ /* Set the private bit to indicate that we've secured this */
+ for_each_sg(sgt->sgl, sg, sgt->nents, i)
+ SetPagePrivate(sg_page(sg));
+
return ret;
err:
diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h
index b687675be02f..f2f2ca11c3fa 100644
--- a/drivers/staging/android/ion/ion_priv.h
+++ b/drivers/staging/android/ion/ion_priv.h
@@ -2,7 +2,7 @@
* drivers/staging/android/ion/ion_priv.h
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -200,8 +200,8 @@ struct ion_heap {
struct task_struct *task;
int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *);
- atomic_t total_allocated;
- atomic_t total_handles;
+ atomic_long_t total_allocated;
+ atomic_long_t total_handles;
};
/**
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 8deee007218b..9e12b2ce3272 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -537,7 +537,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
cache_limit = minfree * (long)(PAGE_SIZE / 1024);
free = other_free * (long)(PAGE_SIZE / 1024);
trace_lowmemory_kill(selected, cache_size, cache_limit, free);
- lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \
+ lowmem_print(1, "Killing '%s' (%d) (tgid %d), adj %hd,\n" \
" to free %ldkB on behalf of '%s' (%d) because\n" \
" cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \
" Free memory is %ldkB above reserved.\n" \
@@ -547,7 +547,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
" Total file cache is %ldkB\n" \
" Total zcache is %ldkB\n" \
" GFP mask is 0x%x\n",
- selected->comm, selected->pid,
+ selected->comm, selected->pid, selected->tgid,
selected_oom_score_adj,
selected_tasksize * (long)(PAGE_SIZE / 1024),
current->comm, current->pid,
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index 7b4af519e17e..b831f08e2769 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -2901,9 +2901,6 @@ static int __init comedi_init(void)
comedi_class->dev_groups = comedi_dev_groups;
- /* XXX requires /proc interface */
- comedi_proc_init();
-
/* create devices files for legacy/manual use */
for (i = 0; i < comedi_num_legacy_minors; i++) {
struct comedi_device *dev;
@@ -2911,6 +2908,7 @@ static int __init comedi_init(void)
dev = comedi_alloc_board_minor(NULL);
if (IS_ERR(dev)) {
comedi_cleanup_board_minors();
+ class_destroy(comedi_class);
cdev_del(&comedi_cdev);
unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
COMEDI_NUM_MINORS);
@@ -2920,6 +2918,9 @@ static int __init comedi_init(void)
mutex_unlock(&dev->mutex);
}
+ /* XXX requires /proc interface */
+ comedi_proc_init();
+
return 0;
}
module_init(comedi_init);
diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
index d97aa2827412..8eb7179da342 100644
--- a/drivers/staging/iio/resolver/ad2s1210.c
+++ b/drivers/staging/iio/resolver/ad2s1210.c
@@ -468,7 +468,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
long m)
{
struct ad2s1210_state *st = iio_priv(indio_dev);
- bool negative;
+ u16 negative;
int ret = 0;
u16 pos;
s16 vel;
diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
index 82a7c27c517f..c2d2c17550a7 100644
--- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c
+++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
@@ -47,7 +47,9 @@ static struct usb_device_id rtw_usb_id_tbl[] = {
{USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */
{USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */
{USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */
+ {USB_DEVICE(0x2357, 0x010c)}, /* TP-Link TL-WN722N v2 */
{USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */
+ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0xffef)}, /* Rosewill RNX-N150NUB */
{} /* Terminating entry */
};
diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c
index 01e642db311e..f35ee85f61b5 100644
--- a/drivers/staging/vt6656/main_usb.c
+++ b/drivers/staging/vt6656/main_usb.c
@@ -529,6 +529,9 @@ static int vnt_start(struct ieee80211_hw *hw)
goto free_all;
}
+ if (vnt_key_init_table(priv))
+ goto free_all;
+
priv->int_interval = 1; /* bInterval is set to 1 */
vnt_int_start_interrupt(priv);
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index a180c000e246..1ff1c83e2df5 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -418,6 +418,7 @@ int iscsit_reset_np_thread(
return 0;
}
np->np_thread_state = ISCSI_NP_THREAD_RESET;
+ atomic_inc(&np->np_reset_count);
if (np->np_thread) {
spin_unlock_bh(&np->np_thread_lock);
@@ -1996,6 +1997,7 @@ iscsit_setup_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
cmd->data_direction = DMA_NONE;
+ kfree(cmd->text_in_ptr);
cmd->text_in_ptr = NULL;
return 0;
@@ -3965,6 +3967,8 @@ int iscsi_target_tx_thread(void *arg)
{
int ret = 0;
struct iscsi_conn *conn = arg;
+ bool conn_freed = false;
+
/*
* Allow ourselves to be interrupted by SIGINT so that a
* connection recovery / failure event can be triggered externally.
@@ -3990,12 +3994,14 @@ get_immediate:
goto transport_err;
ret = iscsit_handle_response_queue(conn);
- if (ret == 1)
+ if (ret == 1) {
goto get_immediate;
- else if (ret == -ECONNRESET)
+ } else if (ret == -ECONNRESET) {
+ conn_freed = true;
goto out;
- else if (ret < 0)
+ } else if (ret < 0) {
goto transport_err;
+ }
}
transport_err:
@@ -4005,8 +4011,13 @@ transport_err:
* responsible for cleaning up the early connection failure.
*/
if (conn->conn_state != TARG_CONN_STATE_IN_LOGIN)
- iscsit_take_action_for_connection_exit(conn);
+ iscsit_take_action_for_connection_exit(conn, &conn_freed);
out:
+ if (!conn_freed) {
+ while (!kthread_should_stop()) {
+ msleep(100);
+ }
+ }
return 0;
}
@@ -4105,6 +4116,7 @@ int iscsi_target_rx_thread(void *arg)
u32 checksum = 0, digest = 0;
struct iscsi_conn *conn = arg;
struct kvec iov;
+ bool conn_freed = false;
/*
* Allow ourselves to be interrupted by SIGINT so that a
* connection recovery / failure event can be triggered externally.
@@ -4116,7 +4128,7 @@ int iscsi_target_rx_thread(void *arg)
*/
rc = wait_for_completion_interruptible(&conn->rx_login_comp);
if (rc < 0 || iscsi_target_check_conn_state(conn))
- return 0;
+ goto out;
if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) {
struct completion comp;
@@ -4201,7 +4213,13 @@ int iscsi_target_rx_thread(void *arg)
transport_err:
if (!signal_pending(current))
atomic_set(&conn->transport_failed, 1);
- iscsit_take_action_for_connection_exit(conn);
+ iscsit_take_action_for_connection_exit(conn, &conn_freed);
+out:
+ if (!conn_freed) {
+ while (!kthread_should_stop()) {
+ msleep(100);
+ }
+ }
return 0;
}
@@ -4575,8 +4593,11 @@ static void iscsit_logout_post_handler_closesession(
* always sleep waiting for RX/TX thread shutdown to complete
* within iscsit_close_connection().
*/
- if (conn->conn_transport->transport_type == ISCSI_TCP)
+ if (conn->conn_transport->transport_type == ISCSI_TCP) {
sleep = cmpxchg(&conn->tx_thread_active, true, false);
+ if (!sleep)
+ return;
+ }
atomic_set(&conn->conn_logout_remove, 0);
complete(&conn->conn_logout_comp);
@@ -4592,8 +4613,11 @@ static void iscsit_logout_post_handler_samecid(
{
int sleep = 1;
- if (conn->conn_transport->transport_type == ISCSI_TCP)
+ if (conn->conn_transport->transport_type == ISCSI_TCP) {
sleep = cmpxchg(&conn->tx_thread_active, true, false);
+ if (!sleep)
+ return;
+ }
atomic_set(&conn->conn_logout_remove, 0);
complete(&conn->conn_logout_comp);
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
index dc1bd1f1bdfe..634ad3662ed6 100644
--- a/drivers/target/iscsi/iscsi_target_configfs.c
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -868,6 +868,7 @@ DEF_TPG_ATTRIB(default_erl);
DEF_TPG_ATTRIB(t10_pi);
DEF_TPG_ATTRIB(fabric_prot_type);
DEF_TPG_ATTRIB(tpg_enabled_sendtargets);
+DEF_TPG_ATTRIB(login_keys_workaround);
static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
&iscsi_tpg_attrib_attr_authentication,
@@ -883,6 +884,7 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
&iscsi_tpg_attrib_attr_t10_pi,
&iscsi_tpg_attrib_attr_fabric_prot_type,
&iscsi_tpg_attrib_attr_tpg_enabled_sendtargets,
+ &iscsi_tpg_attrib_attr_login_keys_workaround,
NULL,
};
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
index 210f6e4830e3..6c88fb021444 100644
--- a/drivers/target/iscsi/iscsi_target_erl0.c
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -930,8 +930,10 @@ static void iscsit_handle_connection_cleanup(struct iscsi_conn *conn)
}
}
-void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn)
+void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn, bool *conn_freed)
{
+ *conn_freed = false;
+
spin_lock_bh(&conn->state_lock);
if (atomic_read(&conn->connection_exit)) {
spin_unlock_bh(&conn->state_lock);
@@ -942,6 +944,7 @@ void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn)
if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) {
spin_unlock_bh(&conn->state_lock);
iscsit_close_connection(conn);
+ *conn_freed = true;
return;
}
@@ -955,4 +958,5 @@ void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn)
spin_unlock_bh(&conn->state_lock);
iscsit_handle_connection_cleanup(conn);
+ *conn_freed = true;
}
diff --git a/drivers/target/iscsi/iscsi_target_erl0.h b/drivers/target/iscsi/iscsi_target_erl0.h
index a9e2f9497fb2..fbc1d84a63c3 100644
--- a/drivers/target/iscsi/iscsi_target_erl0.h
+++ b/drivers/target/iscsi/iscsi_target_erl0.h
@@ -9,6 +9,6 @@ extern int iscsit_stop_time2retain_timer(struct iscsi_session *);
extern void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *);
extern void iscsit_cause_connection_reinstatement(struct iscsi_conn *, int);
extern void iscsit_fall_back_to_erl0(struct iscsi_session *);
-extern void iscsit_take_action_for_connection_exit(struct iscsi_conn *);
+extern void iscsit_take_action_for_connection_exit(struct iscsi_conn *, bool *);
#endif /*** ISCSI_TARGET_ERL0_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 4a137b0ae3dc..bc2cbffec27e 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -1219,9 +1219,11 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
flush_signals(current);
spin_lock_bh(&np->np_thread_lock);
- if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+ if (atomic_dec_if_positive(&np->np_reset_count) >= 0) {
np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
+ spin_unlock_bh(&np->np_thread_lock);
complete(&np->np_restart_comp);
+ return 1;
} else if (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN) {
spin_unlock_bh(&np->np_thread_lock);
goto exit;
@@ -1254,7 +1256,8 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
goto exit;
} else if (rc < 0) {
spin_lock_bh(&np->np_thread_lock);
- if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+ if (atomic_dec_if_positive(&np->np_reset_count) >= 0) {
+ np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
spin_unlock_bh(&np->np_thread_lock);
complete(&np->np_restart_comp);
iscsit_put_transport(conn->conn_transport);
@@ -1436,5 +1439,9 @@ int iscsi_target_login_thread(void *arg)
break;
}
+ while (!kthread_should_stop()) {
+ msleep(100);
+ }
+
return 0;
}
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index 9fc9117d0f22..58c629aec73c 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -489,14 +489,60 @@ static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn)
static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *);
-static bool iscsi_target_sk_state_check(struct sock *sk)
+static bool __iscsi_target_sk_check_close(struct sock *sk)
{
if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) {
- pr_debug("iscsi_target_sk_state_check: TCP_CLOSE_WAIT|TCP_CLOSE,"
+ pr_debug("__iscsi_target_sk_check_close: TCP_CLOSE_WAIT|TCP_CLOSE,"
"returning FALSE\n");
- return false;
+ return true;
}
- return true;
+ return false;
+}
+
+static bool iscsi_target_sk_check_close(struct iscsi_conn *conn)
+{
+ bool state = false;
+
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+
+ read_lock_bh(&sk->sk_callback_lock);
+ state = (__iscsi_target_sk_check_close(sk) ||
+ test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
+ read_unlock_bh(&sk->sk_callback_lock);
+ }
+ return state;
+}
+
+static bool iscsi_target_sk_check_flag(struct iscsi_conn *conn, unsigned int flag)
+{
+ bool state = false;
+
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+
+ read_lock_bh(&sk->sk_callback_lock);
+ state = test_bit(flag, &conn->login_flags);
+ read_unlock_bh(&sk->sk_callback_lock);
+ }
+ return state;
+}
+
+static bool iscsi_target_sk_check_and_clear(struct iscsi_conn *conn, unsigned int flag)
+{
+ bool state = false;
+
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+
+ write_lock_bh(&sk->sk_callback_lock);
+ state = (__iscsi_target_sk_check_close(sk) ||
+ test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
+ if (!state)
+ clear_bit(flag, &conn->login_flags);
+ write_unlock_bh(&sk->sk_callback_lock);
+ }
+ return state;
}
static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login)
@@ -536,6 +582,20 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
conn, current->comm, current->pid);
+ /*
+ * If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
+ * before initial PDU processing in iscsi_target_start_negotiation()
+ * has completed, go ahead and retry until it's cleared.
+ *
+ * Otherwise if the TCP connection drops while this is occuring,
+ * iscsi_target_start_negotiation() will detect the failure, call
+ * cancel_delayed_work_sync(&conn->login_work), and cleanup the
+ * remaining iscsi connection resources from iscsi_np process context.
+ */
+ if (iscsi_target_sk_check_flag(conn, LOGIN_FLAGS_INITIAL_PDU)) {
+ schedule_delayed_work(&conn->login_work, msecs_to_jiffies(10));
+ return;
+ }
spin_lock(&tpg->tpg_state_lock);
state = (tpg->tpg_state == TPG_STATE_ACTIVE);
@@ -543,26 +603,12 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
if (!state) {
pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n");
- iscsi_target_restore_sock_callbacks(conn);
- iscsi_target_login_drop(conn, login);
- iscsit_deaccess_np(np, tpg, tpg_np);
- return;
+ goto err;
}
- if (conn->sock) {
- struct sock *sk = conn->sock->sk;
-
- read_lock_bh(&sk->sk_callback_lock);
- state = iscsi_target_sk_state_check(sk);
- read_unlock_bh(&sk->sk_callback_lock);
-
- if (!state) {
- pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
- iscsi_target_restore_sock_callbacks(conn);
- iscsi_target_login_drop(conn, login);
- iscsit_deaccess_np(np, tpg, tpg_np);
- return;
- }
+ if (iscsi_target_sk_check_close(conn)) {
+ pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
+ goto err;
}
conn->login_kworker = current;
@@ -580,34 +626,29 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
flush_signals(current);
conn->login_kworker = NULL;
- if (rc < 0) {
- iscsi_target_restore_sock_callbacks(conn);
- iscsi_target_login_drop(conn, login);
- iscsit_deaccess_np(np, tpg, tpg_np);
- return;
- }
+ if (rc < 0)
+ goto err;
pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n",
conn, current->comm, current->pid);
rc = iscsi_target_do_login(conn, login);
if (rc < 0) {
- iscsi_target_restore_sock_callbacks(conn);
- iscsi_target_login_drop(conn, login);
- iscsit_deaccess_np(np, tpg, tpg_np);
+ goto err;
} else if (!rc) {
- if (conn->sock) {
- struct sock *sk = conn->sock->sk;
-
- write_lock_bh(&sk->sk_callback_lock);
- clear_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags);
- write_unlock_bh(&sk->sk_callback_lock);
- }
+ if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_READ_ACTIVE))
+ goto err;
} else if (rc == 1) {
iscsi_target_nego_release(conn);
iscsi_post_login_handler(np, conn, zero_tsih);
iscsit_deaccess_np(np, tpg, tpg_np);
}
+ return;
+
+err:
+ iscsi_target_restore_sock_callbacks(conn);
+ iscsi_target_login_drop(conn, login);
+ iscsit_deaccess_np(np, tpg, tpg_np);
}
static void iscsi_target_do_cleanup(struct work_struct *work)
@@ -655,31 +696,54 @@ static void iscsi_target_sk_state_change(struct sock *sk)
orig_state_change(sk);
return;
}
+ state = __iscsi_target_sk_check_close(sk);
+ pr_debug("__iscsi_target_sk_close_change: state: %d\n", state);
+
if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1 sk_state_change"
" conn: %p\n", conn);
+ if (state)
+ set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
write_unlock_bh(&sk->sk_callback_lock);
orig_state_change(sk);
return;
}
- if (test_and_set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
+ if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n",
conn);
write_unlock_bh(&sk->sk_callback_lock);
orig_state_change(sk);
return;
}
+ /*
+ * If the TCP connection has dropped, go ahead and set LOGIN_FLAGS_CLOSED,
+ * but only queue conn->login_work -> iscsi_target_do_login_rx()
+ * processing if LOGIN_FLAGS_INITIAL_PDU has already been cleared.
+ *
+ * When iscsi_target_do_login_rx() runs, iscsi_target_sk_check_close()
+ * will detect the dropped TCP connection from delayed workqueue context.
+ *
+ * If LOGIN_FLAGS_INITIAL_PDU is still set, which means the initial
+ * iscsi_target_start_negotiation() is running, iscsi_target_do_login()
+ * via iscsi_target_sk_check_close() or iscsi_target_start_negotiation()
+ * via iscsi_target_sk_check_and_clear() is responsible for detecting the
+ * dropped TCP connection in iscsi_np process context, and cleaning up
+ * the remaining iscsi connection resources.
+ */
+ if (state) {
+ pr_debug("iscsi_target_sk_state_change got failed state\n");
+ set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
+ state = test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
+ write_unlock_bh(&sk->sk_callback_lock);
- state = iscsi_target_sk_state_check(sk);
- write_unlock_bh(&sk->sk_callback_lock);
-
- pr_debug("iscsi_target_sk_state_change: state: %d\n", state);
+ orig_state_change(sk);
- if (!state) {
- pr_debug("iscsi_target_sk_state_change got failed state\n");
- schedule_delayed_work(&conn->login_cleanup_work, 0);
+ if (!state)
+ schedule_delayed_work(&conn->login_work, 0);
return;
}
+ write_unlock_bh(&sk->sk_callback_lock);
+
orig_state_change(sk);
}
@@ -818,7 +882,8 @@ static int iscsi_target_handle_csg_zero(
SENDER_TARGET,
login->rsp_buf,
&login->rsp_length,
- conn->param_list);
+ conn->param_list,
+ conn->tpg->tpg_attrib.login_keys_workaround);
if (ret < 0)
return -1;
@@ -888,7 +953,8 @@ static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_log
SENDER_TARGET,
login->rsp_buf,
&login->rsp_length,
- conn->param_list);
+ conn->param_list,
+ conn->tpg->tpg_attrib.login_keys_workaround);
if (ret < 0) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_INIT_ERR);
@@ -942,6 +1008,15 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
if (iscsi_target_handle_csg_one(conn, login) < 0)
return -1;
if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
+ /*
+ * Check to make sure the TCP connection has not
+ * dropped asynchronously while session reinstatement
+ * was occuring in this kthread context, before
+ * transitioning to full feature phase operation.
+ */
+ if (iscsi_target_sk_check_close(conn))
+ return -1;
+
login->tsih = conn->sess->tsih;
login->login_complete = 1;
iscsi_target_restore_sock_callbacks(conn);
@@ -968,21 +1043,6 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
break;
}
- if (conn->sock) {
- struct sock *sk = conn->sock->sk;
- bool state;
-
- read_lock_bh(&sk->sk_callback_lock);
- state = iscsi_target_sk_state_check(sk);
- read_unlock_bh(&sk->sk_callback_lock);
-
- if (!state) {
- pr_debug("iscsi_target_do_login() failed state for"
- " conn: %p\n", conn);
- return -1;
- }
- }
-
return 0;
}
@@ -1246,16 +1306,28 @@ int iscsi_target_start_negotiation(
{
int ret;
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+
+ write_lock_bh(&sk->sk_callback_lock);
+ set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
+ set_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
+ write_unlock_bh(&sk->sk_callback_lock);
+ }
+ /*
+ * If iscsi_target_do_login returns zero to signal more PDU
+ * exchanges are required to complete the login, go ahead and
+ * clear LOGIN_FLAGS_INITIAL_PDU but only if the TCP connection
+ * is still active.
+ *
+ * Otherwise if TCP connection dropped asynchronously, go ahead
+ * and perform connection cleanup now.
+ */
ret = iscsi_target_do_login(conn, login);
- if (!ret) {
- if (conn->sock) {
- struct sock *sk = conn->sock->sk;
+ if (!ret && iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
+ ret = -1;
- write_lock_bh(&sk->sk_callback_lock);
- set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
- write_unlock_bh(&sk->sk_callback_lock);
- }
- } else if (ret < 0) {
+ if (ret < 0) {
cancel_delayed_work_sync(&conn->login_work);
cancel_delayed_work_sync(&conn->login_cleanup_work);
iscsi_target_restore_sock_callbacks(conn);
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
index 6d1b0acbc5b3..76bde76edad1 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.c
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -764,7 +764,8 @@ static int iscsi_check_for_auth_key(char *key)
return 0;
}
-static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param)
+static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param,
+ bool keys_workaround)
{
if (IS_TYPE_BOOL_AND(param)) {
if (!strcmp(param->value, NO))
@@ -772,19 +773,31 @@ static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param)
} else if (IS_TYPE_BOOL_OR(param)) {
if (!strcmp(param->value, YES))
SET_PSTATE_REPLY_OPTIONAL(param);
- /*
- * Required for gPXE iSCSI boot client
- */
- if (!strcmp(param->name, IMMEDIATEDATA))
- SET_PSTATE_REPLY_OPTIONAL(param);
+
+ if (keys_workaround) {
+ /*
+ * Required for gPXE iSCSI boot client
+ */
+ if (!strcmp(param->name, IMMEDIATEDATA))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ }
} else if (IS_TYPE_NUMBER(param)) {
if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH))
SET_PSTATE_REPLY_OPTIONAL(param);
- /*
- * Required for gPXE iSCSI boot client
- */
- if (!strcmp(param->name, MAXCONNECTIONS))
- SET_PSTATE_REPLY_OPTIONAL(param);
+
+ if (keys_workaround) {
+ /*
+ * Required for Mellanox Flexboot PXE boot ROM
+ */
+ if (!strcmp(param->name, FIRSTBURSTLENGTH))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+
+ /*
+ * Required for gPXE iSCSI boot client
+ */
+ if (!strcmp(param->name, MAXCONNECTIONS))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ }
} else if (IS_PHASE_DECLARATIVE(param))
SET_PSTATE_REPLY_OPTIONAL(param);
}
@@ -1421,7 +1434,8 @@ int iscsi_encode_text_output(
u8 sender,
char *textbuf,
u32 *length,
- struct iscsi_param_list *param_list)
+ struct iscsi_param_list *param_list,
+ bool keys_workaround)
{
char *output_buf = NULL;
struct iscsi_extra_response *er;
@@ -1457,7 +1471,8 @@ int iscsi_encode_text_output(
*length += 1;
output_buf = textbuf + *length;
SET_PSTATE_PROPOSER(param);
- iscsi_check_proposer_for_optional_reply(param);
+ iscsi_check_proposer_for_optional_reply(param,
+ keys_workaround);
pr_debug("Sending key: %s=%s\n",
param->name, param->value);
}
diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h
index a0751e3f0813..17a58c2913f2 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.h
+++ b/drivers/target/iscsi/iscsi_target_parameters.h
@@ -40,7 +40,7 @@ extern int iscsi_extract_key_value(char *, char **, char **);
extern int iscsi_update_param_value(struct iscsi_param *, char *);
extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsi_conn *);
extern int iscsi_encode_text_output(u8, u8, char *, u32 *,
- struct iscsi_param_list *);
+ struct iscsi_param_list *, bool);
extern int iscsi_check_negotiated_keys(struct iscsi_param_list *);
extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *,
struct iscsi_param_list *);
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
index 205a509b0dfb..63e1dcc5914d 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.c
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -227,6 +227,7 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg)
a->t10_pi = TA_DEFAULT_T10_PI;
a->fabric_prot_type = TA_DEFAULT_FABRIC_PROT_TYPE;
a->tpg_enabled_sendtargets = TA_DEFAULT_TPG_ENABLED_SENDTARGETS;
+ a->login_keys_workaround = TA_DEFAULT_LOGIN_KEYS_WORKAROUND;
}
int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg)
@@ -899,3 +900,21 @@ int iscsit_ta_tpg_enabled_sendtargets(
return 0;
}
+
+int iscsit_ta_login_keys_workaround(
+ struct iscsi_portal_group *tpg,
+ u32 flag)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((flag != 0) && (flag != 1)) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+
+ a->login_keys_workaround = flag;
+ pr_debug("iSCSI_TPG[%hu] - TPG enabled bit for login keys workaround: %s ",
+ tpg->tpgt, (a->login_keys_workaround) ? "ON" : "OFF");
+
+ return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h
index 2da211920c18..901a712180f0 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.h
+++ b/drivers/target/iscsi/iscsi_target_tpg.h
@@ -39,5 +39,6 @@ extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32);
extern int iscsit_ta_t10_pi(struct iscsi_portal_group *, u32);
extern int iscsit_ta_fabric_prot_type(struct iscsi_portal_group *, u32);
extern int iscsit_ta_tpg_enabled_sendtargets(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_login_keys_workaround(struct iscsi_portal_group *, u32);
#endif /* ISCSI_TARGET_TPG_H */
diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c
index f916d18ccb48..b070ddf1dc37 100644
--- a/drivers/target/target_core_fabric_configfs.c
+++ b/drivers/target/target_core_fabric_configfs.c
@@ -92,6 +92,11 @@ static int target_fabric_mappedlun_link(
pr_err("Source se_lun->lun_se_dev does not exist\n");
return -EINVAL;
}
+ if (lun->lun_shutdown) {
+ pr_err("Unable to create mappedlun symlink because"
+ " lun->lun_shutdown=true\n");
+ return -EINVAL;
+ }
se_tpg = lun->lun_tpg;
nacl_ci = &lun_acl_ci->ci_parent->ci_group->cg_item;
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index 899c33b3c734..f69f4902dc07 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -673,6 +673,8 @@ void core_tpg_remove_lun(
*/
struct se_device *dev = rcu_dereference_raw(lun->lun_se_dev);
+ lun->lun_shutdown = true;
+
core_clear_lun_from_tpg(lun, tpg);
/*
* Wait for any active I/O references to percpu se_lun->lun_ref to
@@ -694,6 +696,8 @@ void core_tpg_remove_lun(
}
if (!(dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE))
hlist_del_rcu(&lun->link);
+
+ lun->lun_shutdown = false;
mutex_unlock(&tpg->tpg_lun_mutex);
percpu_ref_exit(&lun->lun_ref);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 37c77db6e737..f71bedea973a 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -728,6 +728,15 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
if (cmd->transport_state & CMD_T_ABORTED ||
cmd->transport_state & CMD_T_STOP) {
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ /*
+ * If COMPARE_AND_WRITE was stopped by __transport_wait_for_tasks(),
+ * release se_device->caw_sem obtained by sbc_compare_and_write()
+ * since target_complete_ok_work() or target_complete_failure_work()
+ * won't be called to invoke the normal CAW completion callbacks.
+ */
+ if (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) {
+ up(&dev->caw_sem);
+ }
complete_all(&cmd->t_transport_stop_comp);
return;
} else if (!success) {
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index 65c7033e0df0..632fa8c1260b 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -192,8 +192,10 @@ unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq)
mutex_lock(&cooling_list_lock);
list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
if (cpumask_test_cpu(cpu, &cpufreq_dev->allowed_cpus)) {
+ unsigned long level = get_level(cpufreq_dev, freq);
+
mutex_unlock(&cooling_list_lock);
- return get_level(cpufreq_dev, freq);
+ return level;
}
}
mutex_unlock(&cooling_list_lock);
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index 4b586f62cdc7..bca85bf2f7ec 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",
@@ -4996,7 +4997,7 @@ static ssize_t __ref store_cc_enabled(struct kobject *kobj,
hotplug_init_cpu_offlined();
mutex_lock(&core_control_mutex);
update_offline_cores(cpus_offlined);
- if (hotplug_enabled) {
+ if (hotplug_enabled && hotplug_task) {
for_each_possible_cpu(cpu) {
if (!(msm_thermal_info.core_control_mask &
BIT(cpus[cpu].cpu)))
@@ -5379,7 +5380,7 @@ int msm_thermal_init(struct msm_thermal_data *pdata)
if (ret)
pr_err("cannot register cpufreq notifier. err:%d\n", ret);
- if (!lmh_dcvs_available) {
+ if (!lmh_dcvs_is_supported) {
register_reboot_notifier(&msm_thermal_reboot_notifier);
pm_notifier(msm_thermal_suspend_callback, 0);
}
@@ -7414,6 +7415,7 @@ static int msm_thermal_dev_probe(struct platform_device *pdev)
if (ret)
goto probe_exit;
+ lmh_dcvs_is_supported = of_property_read_bool(node, "clock-names");
probe_cc(node, &data, pdev);
probe_freq_mitigation(node, &data, pdev);
probe_cx_phase_ctrl(node, &data, pdev);
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index fc9faaee3170..b80e75fc3521 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -2255,7 +2255,7 @@ void disable_wakeup_interrupt(struct msm_hs_port *msm_uport)
return;
if (msm_uport->wakeup.enabled) {
- disable_irq_nosync(msm_uport->wakeup.irq);
+ disable_irq(msm_uport->wakeup.irq);
enable_irq(uport->irq);
spin_lock_irqsave(&uport->lock, flags);
msm_uport->wakeup.enabled = false;
@@ -2614,8 +2614,7 @@ static int msm_hs_startup(struct uart_port *uport)
msm_hs_resource_vote(msm_uport);
if (is_use_low_power_wakeup(msm_uport)) {
- ret = request_threaded_irq(msm_uport->wakeup.irq, NULL,
- msm_hs_wakeup_isr,
+ ret = request_irq(msm_uport->wakeup.irq, msm_hs_wakeup_isr,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"msm_hs_wakeup", msm_uport);
if (unlikely(ret)) {
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 5ab54ef4f304..e4f69bddcfb1 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2708,13 +2708,13 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
* related to the kernel should not use this.
*/
data = vt_get_shift_state();
- ret = __put_user(data, p);
+ ret = put_user(data, p);
break;
case TIOCL_GETMOUSEREPORTING:
console_lock(); /* May be overkill */
data = mouse_reporting();
console_unlock();
- ret = __put_user(data, p);
+ ret = put_user(data, p);
break;
case TIOCL_SETVESABLANK:
console_lock();
@@ -2723,7 +2723,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
break;
case TIOCL_GETKMSGREDIRECT:
data = vt_get_kmsg_redirect();
- ret = __put_user(data, p);
+ ret = put_user(data, p);
break;
case TIOCL_SETKMSGREDIRECT:
if (!capable(CAP_SYS_ADMIN)) {
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 0b7194086c5a..df96f5f88c15 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1759,6 +1759,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
},
+ { USB_DEVICE(0xfff0, 0x0100), /* DATECS FP-2000 */
+ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
+ },
{ USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */
.driver_info = CLEAR_HALT_CONDITIONS,
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index bc5a6966dda9..c31c753b6e28 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1851,7 +1851,7 @@ void usb_hcd_flush_endpoint(struct usb_device *udev,
/* No more submits can occur */
spin_lock_irq(&hcd_urb_list_lock);
rescan:
- list_for_each_entry (urb, &ep->urb_list, urb_list) {
+ list_for_each_entry_reverse(urb, &ep->urb_list, urb_list) {
int is_in;
if (urb->unlinked)
@@ -2505,6 +2505,8 @@ void usb_hc_died (struct usb_hcd *hcd)
}
if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) {
hcd = hcd->shared_hcd;
+ clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
+ set_bit(HCD_FLAG_DEAD, &hcd->flags);
if (hcd->rh_registered) {
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 5de22f4892cd..370ad9690349 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -4674,7 +4674,8 @@ hub_power_remaining(struct usb_hub *hub)
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
u16 portchange)
{
- int status, i;
+ int status = -ENODEV;
+ int i;
unsigned unit_load;
struct usb_device *hdev = hub->hdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
@@ -4878,9 +4879,10 @@ loop:
done:
hub_port_disable(hub, port1, 1);
- if (hcd->driver->relinquish_port && !hub->hdev->parent)
- hcd->driver->relinquish_port(hcd, port1);
-
+ if (hcd->driver->relinquish_port && !hub->hdev->parent) {
+ if (status != -ENOTCONN && status != -ENODEV)
+ hcd->driver->relinquish_port(hcd, port1);
+ }
}
/* Handle physical or logical connection change events.
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 96b21b0dac1e..574da2b4529c 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -150,6 +150,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* appletouch */
{ USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Genesys Logic hub, internally used by Moshi USB to Ethernet Adapter */
+ { USB_DEVICE(0x05e3, 0x0616), .driver_info = USB_QUIRK_NO_LPM },
+
/* Avision AV600U */
{ USB_DEVICE(0x0638, 0x0a13), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
@@ -223,6 +226,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Blackmagic Design UltraStudio SDI */
{ USB_DEVICE(0x1edb, 0xbd4f), .driver_info = USB_QUIRK_NO_LPM },
+ /* Hauppauge HVR-950q */
+ { USB_DEVICE(0x2040, 0x7200), .driver_info =
+ USB_QUIRK_CONFIG_INTF_STRINGS },
+
/* INTEL VALUE SSD */
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -245,6 +252,7 @@ static const struct usb_device_id usb_amd_resume_quirk_list[] = {
{ USB_DEVICE(0x093a, 0x2500), .driver_info = USB_QUIRK_RESET_RESUME },
{ USB_DEVICE(0x093a, 0x2510), .driver_info = USB_QUIRK_RESET_RESUME },
{ USB_DEVICE(0x093a, 0x2521), .driver_info = USB_QUIRK_RESET_RESUME },
+ { USB_DEVICE(0x03f0, 0x2b4a), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Optical Mouse M90/M100 */
{ USB_DEVICE(0x046d, 0xc05a), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 2776cfe64c09..ef9cf4a21afe 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -127,6 +127,22 @@ out:
*/
#define USB_ACPI_LOCATION_VALID (1 << 31)
+static struct acpi_device *usb_acpi_find_port(struct acpi_device *parent,
+ int raw)
+{
+ struct acpi_device *adev;
+
+ if (!parent)
+ return NULL;
+
+ list_for_each_entry(adev, &parent->children, node) {
+ if (acpi_device_adr(adev) == raw)
+ return adev;
+ }
+
+ return acpi_find_child_device(parent, raw, false);
+}
+
static struct acpi_device *usb_acpi_find_companion(struct device *dev)
{
struct usb_device *udev;
@@ -174,8 +190,10 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev)
int raw;
raw = usb_hcd_find_raw_port_number(hcd, port1);
- adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev),
- raw, false);
+
+ adev = usb_acpi_find_port(ACPI_COMPANION(&udev->dev),
+ raw);
+
if (!adev)
return NULL;
} else {
@@ -186,7 +204,9 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev)
return NULL;
acpi_bus_get_device(parent_handle, &adev);
- adev = acpi_find_child_device(adev, port1, false);
+
+ adev = usb_acpi_find_port(adev, port1);
+
if (!adev)
return NULL;
}
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 c3077ac11709..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");
@@ -2634,8 +2636,6 @@ done:
static void check_for_sdp_connection(struct work_struct *w)
{
- int ret;
- union power_supply_propval pval = {0};
struct dwc3_msm *mdwc =
container_of(w, struct dwc3_msm, sdp_check.work);
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
@@ -2646,21 +2646,6 @@ static void check_for_sdp_connection(struct work_struct *w)
/* floating D+/D- lines detected */
if (dwc->gadget.state < USB_STATE_DEFAULT &&
dwc3_gadget_get_link_state(dwc) != DWC3_LINK_STATE_CMPLY) {
- if (!mdwc->usb_psy) {
- mdwc->usb_psy = power_supply_get_by_name("usb");
- if (!mdwc->usb_psy) {
- dev_dbg(mdwc->dev,
- "Could not get usb power_supply\n");
- return;
- }
- }
- pval.intval = -ETIMEDOUT;
- ret = power_supply_set_property(mdwc->usb_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
- if (ret)
- dev_dbg(mdwc->dev,
- "power supply error when setting property\n");
-
mdwc->vbus_active = 0;
dbg_event(0xFF, "Q RW SPD CHK", mdwc->vbus_active);
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
@@ -2915,7 +2900,7 @@ static int dwc3_msm_probe(struct platform_device *pdev)
ret = dwc3_msm_get_clk_gdsc(mdwc);
if (ret) {
dev_err(&pdev->dev, "error getting clock or gdsc.\n");
- return ret;
+ goto err;
}
mdwc->id_state = DWC3_ID_FLOAT;
@@ -2962,8 +2947,9 @@ static int dwc3_msm_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(&pdev->dev, mdwc->ss_phy_irq,
msm_dwc3_pwr_irq,
msm_dwc3_pwr_irq_thread,
- IRQF_TRIGGER_RISING | IRQF_EARLY_RESUME
- | IRQF_ONESHOT, "ss_phy_irq", mdwc);
+ IRQF_TRIGGER_HIGH | IRQ_TYPE_LEVEL_HIGH
+ | IRQF_EARLY_RESUME | IRQF_ONESHOT,
+ "ss_phy_irq", mdwc);
if (ret) {
dev_err(&pdev->dev, "irqreq ss_phy_irq failed: %d\n",
ret);
@@ -3223,19 +3209,14 @@ static int dwc3_msm_probe(struct platform_device *pdev)
return 0;
put_dwc3:
- platform_device_put(mdwc->dwc3);
if (mdwc->bus_perf_client)
msm_bus_scale_unregister_client(mdwc->bus_perf_client);
+ of_platform_depopulate(&pdev->dev);
err:
+ destroy_workqueue(mdwc->dwc3_wq);
return ret;
}
-static int dwc3_msm_remove_children(struct device *dev, void *data)
-{
- device_unregister(dev);
- return 0;
-}
-
static int dwc3_msm_remove(struct platform_device *pdev)
{
struct dwc3_msm *mdwc = platform_get_drvdata(pdev);
@@ -3272,8 +3253,7 @@ static int dwc3_msm_remove(struct platform_device *pdev)
if (mdwc->hs_phy)
mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
- platform_device_put(mdwc->dwc3);
- device_for_each_child(&pdev->dev, NULL, dwc3_msm_remove_children);
+ of_platform_depopulate(&pdev->dev);
dbg_event(0xFF, "Remov put", 0);
pm_runtime_disable(mdwc->dev);
@@ -3456,13 +3436,16 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
dev_dbg(mdwc->dev, "%s: turn on host\n", __func__);
mdwc->hs_phy->flags |= PHY_HOST_MODE;
- if (dwc->maximum_speed == USB_SPEED_SUPER)
+ if (dwc->maximum_speed == USB_SPEED_SUPER) {
mdwc->ss_phy->flags |= PHY_HOST_MODE;
+ usb_phy_notify_connect(mdwc->ss_phy,
+ USB_SPEED_SUPER);
+ }
+ usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
pm_runtime_get_sync(mdwc->dev);
dbg_event(0xFF, "StrtHost gync",
atomic_read(&mdwc->dev->power.usage_count));
- usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
if (!IS_ERR(mdwc->vbus_reg))
ret = regulator_enable(mdwc->vbus_reg);
if (ret) {
@@ -3570,8 +3553,13 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
dbg_event(0xFF, "StopHost gsync",
atomic_read(&mdwc->dev->power.usage_count));
usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH);
+ if (mdwc->ss_phy->flags & PHY_HOST_MODE) {
+ usb_phy_notify_disconnect(mdwc->ss_phy,
+ USB_SPEED_SUPER);
+ mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
+ }
+
mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
- mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
platform_device_del(dwc->xhci);
usb_unregister_notify(&mdwc->host_nb);
@@ -3708,16 +3696,18 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA)
return 0;
psy_type = get_psy_type(mdwc);
- if (psy_type != POWER_SUPPLY_TYPE_USB &&
- psy_type != POWER_SUPPLY_TYPE_USB_FLOAT)
+ if (psy_type == POWER_SUPPLY_TYPE_USB) {
+ dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
+ /* Set max current limit in uA */
+ pval.intval = 1000 * mA;
+ } else if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
+ pval.intval = -ETIMEDOUT;
+ } else {
return 0;
+ }
- dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
-
- /* Set max current limit in uA */
- pval.intval = 1000 * mA;
ret = power_supply_set_property(mdwc->usb_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
+ POWER_SUPPLY_PROP_SDP_CURRENT_MAX, &pval);
if (ret) {
dev_dbg(mdwc->dev, "power supply error when setting property\n");
return ret;
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index 5c0adb9c6fb2..81db2fa08cad 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -224,7 +224,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
dwc3_data->syscfg_reg_off = res->start;
- dev_vdbg(&pdev->dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n",
+ dev_vdbg(&pdev->dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
dwc3_data->rstc_pwrdn = devm_reset_control_get(dev, "powerdown");
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index c2a6fdbfcfee..c244d908fa4f 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -439,6 +439,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
dwc->ep0_usb_req.request.length = sizeof(*response_pkt);
dwc->ep0_usb_req.request.buf = dwc->setup_buf;
dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl;
+ dwc->ep0_usb_req.request.dma = DMA_ERROR_CODE;
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
}
@@ -729,6 +730,7 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket;
dwc->ep0_usb_req.request.buf = dwc->setup_buf;
dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl;
+ dwc->ep0_usb_req.request.dma = DMA_ERROR_CODE;
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
}
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index a2c14bb5efa4..255a11f595c4 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1540,6 +1540,18 @@ static void android_disconnect(struct usb_gadget *gadget)
gi = container_of(cdev, struct gadget_info, cdev);
+ /* FIXME: There's a race between usb_gadget_udc_stop() which is likely
+ * to set the gadget driver to NULL in the udc driver and this drivers
+ * gadget disconnect fn which likely checks for the gadget driver to
+ * be a null ptr. It happens that unbind (doing set_gadget_data(NULL))
+ * is called before the gadget driver is set to NULL and the udc driver
+ * calls disconnect fn which results in cdev being a null ptr.
+ */
+ if (cdev == NULL) {
+ WARN(1, "%s: gadget driver already disconnected\n", __func__);
+ return;
+ }
+
/* accessory HID support can be active while the
accessory function is not actually enabled,
so we need to inform it when we are disconnected.
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index 7950f25136a5..a412f024d834 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -865,6 +865,14 @@ static struct hid_driver acc_hid_driver = {
.probe = acc_hid_probe,
};
+static void acc_complete_setup_noop(struct usb_ep *ep, struct usb_request *req)
+{
+ /*
+ * Default no-op function when nothing needs to be done for the
+ * setup request
+ */
+}
+
int acc_ctrlrequest(struct usb_composite_dev *cdev,
const struct usb_ctrlrequest *ctrl)
{
@@ -892,6 +900,7 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev,
schedule_delayed_work(
&dev->start_work, msecs_to_jiffies(10));
value = 0;
+ cdev->req->complete = acc_complete_setup_noop;
} else if (b_request == ACCESSORY_SEND_STRING) {
dev->string_index = w_index;
cdev->gadget->ep0->driver_data = dev;
@@ -900,10 +909,13 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev,
} else if (b_request == ACCESSORY_SET_AUDIO_MODE &&
w_index == 0 && w_length == 0) {
dev->audio_mode = w_value;
+ cdev->req->complete = acc_complete_setup_noop;
value = 0;
} else if (b_request == ACCESSORY_REGISTER_HID) {
+ cdev->req->complete = acc_complete_setup_noop;
value = acc_register_hid(dev, w_value, w_index);
} else if (b_request == ACCESSORY_UNREGISTER_HID) {
+ cdev->req->complete = acc_complete_setup_noop;
value = acc_unregister_hid(dev, w_value);
} else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) {
spin_lock_irqsave(&dev->lock, flags);
@@ -938,7 +950,7 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev,
if (b_request == ACCESSORY_GET_PROTOCOL) {
*((u16 *)cdev->req->buf) = PROTOCOL_VERSION;
value = sizeof(u16);
-
+ cdev->req->complete = acc_complete_setup_noop;
/* clear any string left over from a previous session */
memset(dev->manufacturer, 0, sizeof(dev->manufacturer));
memset(dev->model, 0, sizeof(dev->model));
diff --git a/drivers/usb/gadget/function/f_ccid.c b/drivers/usb/gadget/function/f_ccid.c
index 28ac8d0010d8..1a281833eadd 100644
--- a/drivers/usb/gadget/function/f_ccid.c
+++ b/drivers/usb/gadget/function/f_ccid.c
@@ -26,7 +26,7 @@
#include "f_ccid.h"
#define BULK_IN_BUFFER_SIZE sizeof(struct ccid_bulk_in_header)
-#define BULK_OUT_BUFFER_SIZE sizeof(struct ccid_bulk_out_header)
+#define BULK_OUT_BUFFER_SIZE 1024
#define CTRL_BUF_SIZE 4
#define FUNCTION_NAME "ccid"
#define MAX_INST_NAME_LEN 40
@@ -629,14 +629,14 @@ static ssize_t ccid_bulk_read(struct file *fp, char __user *buf,
struct f_ccid *ccid_dev = fp->private_data;
struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
struct usb_request *req;
- int r = count, xfer;
+ int r = count, xfer, len;
int ret;
unsigned long flags;
pr_debug("ccid_bulk_read(%zu)\n", count);
if (count > BULK_OUT_BUFFER_SIZE) {
- pr_err("%s: max_buffer_size:%zu given_pkt_size:%zu\n",
+ pr_err("%s: max_buffer_size:%d given_pkt_size:%zu\n",
__func__, BULK_OUT_BUFFER_SIZE, count);
return -ENOMEM;
}
@@ -647,6 +647,7 @@ static ssize_t ccid_bulk_read(struct file *fp, char __user *buf,
goto done;
}
+ len = ALIGN(count, ccid_dev->out->maxpacket);
requeue_req:
spin_lock_irqsave(&ccid_dev->lock, flags);
if (!atomic_read(&ccid_dev->online)) {
@@ -655,7 +656,7 @@ requeue_req:
}
/* queue a request */
req = bulk_dev->rx_req;
- req->length = count;
+ req->length = len;
bulk_dev->rx_done = 0;
spin_unlock_irqrestore(&ccid_dev->lock, flags);
ret = usb_ep_queue(ccid_dev->out, req, GFP_KERNEL);
@@ -688,6 +689,9 @@ requeue_req:
spin_unlock_irqrestore(&ccid_dev->lock, flags);
goto requeue_req;
}
+ if (req->actual > count)
+ pr_err("%s More data received(%d) than required(%zu)\n",
+ __func__, req->actual, count);
xfer = (req->actual < count) ? req->actual : count;
atomic_set(&bulk_dev->rx_req_busy, 1);
spin_unlock_irqrestore(&ccid_dev->lock, flags);
@@ -875,7 +879,8 @@ static ssize_t ccid_ctrl_read(struct file *fp, char __user *buf,
count = CTRL_BUF_SIZE;
ret = wait_event_interruptible(ctrl_dev->tx_wait_q,
- ctrl_dev->tx_ctrl_done);
+ ctrl_dev->tx_ctrl_done ||
+ !atomic_read(&ccid_dev->online));
if (ret < 0)
return ret;
ctrl_dev->tx_ctrl_done = 0;
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 3f903d4776b4..1900870eee39 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -1579,6 +1579,12 @@ static void gsi_rndis_command_complete(struct usb_ep *ep,
struct f_gsi *rndis = req->context;
int status;
+ if (req->status != 0) {
+ log_event_err("RNDIS command completion error %d\n",
+ req->status);
+ return;
+ }
+
status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
if (status < 0)
log_event_err("RNDIS command error %d, %d/%d",
@@ -2501,6 +2507,16 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f)
DEFAULT_PKT_ALIGNMENT_FACTOR);
rndis_set_pkt_alignment_factor(gsi->params,
DEFAULT_PKT_ALIGNMENT_FACTOR);
+ if (gsi->rndis_use_wceis) {
+ info.iad_desc->bFunctionClass =
+ USB_CLASS_WIRELESS_CONTROLLER;
+ info.iad_desc->bFunctionSubClass = 0x01;
+ info.iad_desc->bFunctionProtocol = 0x03;
+ info.ctrl_desc->bInterfaceClass =
+ USB_CLASS_WIRELESS_CONTROLLER;
+ info.ctrl_desc->bInterfaceSubClass = 0x1;
+ info.ctrl_desc->bInterfaceProtocol = 0x03;
+ }
break;
case IPA_USB_MBIM:
info.string_defs = mbim_gsi_string_defs;
@@ -2980,6 +2996,42 @@ static ssize_t gsi_info_show(struct config_item *item, char *page)
CONFIGFS_ATTR_RO(gsi_, info);
+static ssize_t gsi_rndis_wceis_show(struct config_item *item, char *page)
+{
+ struct f_gsi *gsi = to_gsi_opts(item)->gsi;
+
+ return snprintf(page, PAGE_SIZE, "%d\n", gsi->rndis_use_wceis);
+}
+
+static ssize_t gsi_rndis_wceis_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_gsi *gsi = to_gsi_opts(item)->gsi;
+ bool val;
+
+ if (kstrtobool(page, &val))
+ return -EINVAL;
+
+ gsi->rndis_use_wceis = val;
+
+ return len;
+}
+
+CONFIGFS_ATTR(gsi_, rndis_wceis);
+
+static struct configfs_attribute *gsi_rndis_attrs[] = {
+ &gsi_attr_info,
+ &gsi_attr_rndis_wceis,
+ NULL,
+};
+
+static struct config_item_type gsi_func_rndis_type = {
+ .ct_item_ops = &gsi_item_ops,
+ .ct_attrs = gsi_rndis_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+
static struct configfs_attribute *gsi_attrs[] = {
&gsi_attr_info,
NULL,
@@ -3009,6 +3061,9 @@ static int gsi_set_inst_name(struct usb_function_instance *fi,
return -EINVAL;
}
+ if (ret == IPA_USB_RNDIS)
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &gsi_func_rndis_type);
gsi = gsi_function_init(ret);
if (IS_ERR(gsi))
return PTR_ERR(gsi);
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index 262a60e8a450..14490ab296c2 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -237,6 +237,7 @@ struct f_gsi {
struct rndis_params *params;
atomic_t connected;
bool data_interface_up;
+ bool rndis_use_wceis;
const struct usb_endpoint_descriptor *in_ep_desc_backup;
const struct usb_endpoint_descriptor *out_ep_desc_backup;
@@ -470,9 +471,9 @@ static struct usb_interface_descriptor rndis_gsi_control_intf = {
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER,
- .bInterfaceSubClass = 0x01,
- .bInterfaceProtocol = 0x03,
+ .bInterfaceClass = USB_CLASS_MISC,
+ .bInterfaceSubClass = 0x04,
+ .bInterfaceProtocol = 0x01, /* RNDIS over Ethernet */
/* .iInterface = DYNAMIC */
};
@@ -530,9 +531,9 @@ rndis_gsi_iad_descriptor = {
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 0, /* XXX, hardcoded */
.bInterfaceCount = 2, /* control + data */
- .bFunctionClass = USB_CLASS_WIRELESS_CONTROLLER,
- .bFunctionSubClass = 0x01,
- .bFunctionProtocol = 0x03,
+ .bFunctionClass = USB_CLASS_MISC,
+ .bFunctionSubClass = 0x04,
+ .bFunctionProtocol = 0x01, /* RNDIS over Ethernet */
/* .iFunction = DYNAMIC */
};
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 99285b416308..ee579ba2b59e 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -539,7 +539,7 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
}
status = usb_ep_enable(hidg->out_ep);
if (status < 0) {
- ERROR(cdev, "Enable IN endpoint FAILED!\n");
+ ERROR(cdev, "Enable OUT endpoint FAILED!\n");
goto fail;
}
hidg->out_ep->driver_data = hidg;
diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c
index 434af820e827..a28bcd084dc3 100644
--- a/drivers/usb/gadget/function/f_qc_rndis.c
+++ b/drivers/usb/gadget/function/f_qc_rndis.c
@@ -545,6 +545,12 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
rndis_init_msg_type *buf;
u32 ul_max_xfer_size, dl_max_xfer_size;
+ if (req->status != 0) {
+ pr_err("%s: RNDIS command completion error %d\n",
+ __func__, req->status);
+ return;
+ }
+
spin_lock(&rndis_lock);
rndis = _rndis_qc;
if (!rndis || !rndis->notify || !rndis->notify->driver_data) {
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 13888821109d..0917bc500023 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -463,6 +463,12 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
int status;
rndis_init_msg_type *buf;
+ if (req->status != 0) {
+ pr_err("%s: RNDIS command completion error:%d\n",
+ __func__, req->status);
+ return;
+ }
+
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
// spin_lock(&dev->lock);
status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index f9400564cb72..03b9a372636f 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -89,6 +89,7 @@ enum amd_chipset_gen {
AMD_CHIPSET_HUDSON2,
AMD_CHIPSET_BOLTON,
AMD_CHIPSET_YANGTZE,
+ AMD_CHIPSET_TAISHAN,
AMD_CHIPSET_UNKNOWN,
};
@@ -132,6 +133,11 @@ static int amd_chipset_sb_type_init(struct amd_chipset_info *pinfo)
pinfo->sb_type.gen = AMD_CHIPSET_SB700;
else if (rev >= 0x40 && rev <= 0x4f)
pinfo->sb_type.gen = AMD_CHIPSET_SB800;
+ }
+ pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x145c, NULL);
+ if (pinfo->smbus_dev) {
+ pinfo->sb_type.gen = AMD_CHIPSET_TAISHAN;
} else {
pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
@@ -251,11 +257,12 @@ int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev)
{
/* Make sure amd chipset type has already been initialized */
usb_amd_find_chipset_info();
- if (amd_chipset.sb_type.gen != AMD_CHIPSET_YANGTZE)
- return 0;
-
- dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n");
- return 1;
+ if (amd_chipset.sb_type.gen == AMD_CHIPSET_YANGTZE ||
+ amd_chipset.sb_type.gen == AMD_CHIPSET_TAISHAN) {
+ dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n");
+ return 1;
+ }
+ return 0;
}
EXPORT_SYMBOL_GPL(usb_hcd_amd_remote_wakeup_quirk);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 9dbd7595a7a3..fae1222d4bc8 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -783,6 +783,9 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
clear_bit(wIndex, &bus_state->resuming_ports);
set_bit(wIndex, &bus_state->rexit_ports);
+
+ xhci_test_and_clear_bit(xhci, port_array, wIndex,
+ PORT_PLC);
xhci_set_link_state(xhci, port_array, wIndex,
XDEV_U0);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 12e38cf022ff..5bfe47816ac3 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -856,13 +856,16 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
(ep->ep_state & EP_GETTING_NO_STREAMS)) {
int stream_id;
- for (stream_id = 0; stream_id < ep->stream_info->num_streams;
+ for (stream_id = 1; stream_id < ep->stream_info->num_streams;
stream_id++) {
+ ring = ep->stream_info->stream_rings[stream_id];
+ if (!ring)
+ continue;
+
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Killing URBs for slot ID %u, ep index %u, stream %u",
- slot_id, ep_index, stream_id + 1);
- xhci_kill_ring_urbs(xhci,
- ep->stream_info->stream_rings[stream_id]);
+ slot_id, ep_index, stream_id);
+ xhci_kill_ring_urbs(xhci, ring);
}
} else {
ring = ep->ring;
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 13d5614f37f1..0d843e0f8055 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -138,6 +138,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
"Could not flush host TX%d fifo: csr: %04x\n",
ep->epnum, csr))
return;
+ mdelay(1);
}
}
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 0cbe95304417..f9f47da8a88b 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -481,21 +481,18 @@ static inline void pd_reset_protocol(struct usbpd *pd)
pd->send_dr_swap = false;
}
-static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data,
- size_t num_data, enum pd_msg_type type)
+static int pd_send_msg(struct usbpd *pd, u8 msg_type, const u32 *data,
+ size_t num_data, enum pd_sop_type sop)
{
int ret;
u16 hdr;
- hdr = PD_MSG_HDR(hdr_type, pd->current_dr, pd->current_pr,
+ hdr = PD_MSG_HDR(msg_type, pd->current_dr, pd->current_pr,
pd->tx_msgid, num_data, pd->spec_rev);
- ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), type, 15);
- /* TODO figure out timeout. based on tReceive=1.1ms x nRetryCount? */
- if (ret < 0)
+ ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), sop);
+ if (ret)
return ret;
- else if (ret != num_data * sizeof(u32))
- return -EIO;
pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID;
return 0;
@@ -596,7 +593,7 @@ static void pd_send_hard_reset(struct usbpd *pd)
/* Force CC logic to source/sink to keep Rp/Rd unchanged */
set_power_role(pd, pd->current_pr);
pd->hard_reset_count++;
- pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */
+ pd_phy_signal(HARD_RESET_SIG);
pd->in_pr_swap = false;
power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val);
}
@@ -612,12 +609,12 @@ static void kick_sm(struct usbpd *pd, int ms)
queue_work(pd->wq, &pd->sm_work);
}
-static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
+static void phy_sig_received(struct usbpd *pd, enum pd_sig_type sig)
{
union power_supply_propval val = {1};
- if (type != HARD_RESET_SIG) {
- usbpd_err(&pd->dev, "invalid signal (%d) received\n", type);
+ if (sig != HARD_RESET_SIG) {
+ usbpd_err(&pd->dev, "invalid signal (%d) received\n", sig);
return;
}
@@ -632,16 +629,16 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
kick_sm(pd, 0);
}
-static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
+static void phy_msg_received(struct usbpd *pd, enum pd_sop_type sop,
u8 *buf, size_t len)
{
struct rx_msg *rx_msg;
unsigned long flags;
u16 header;
- if (type != SOP_MSG) {
+ if (sop != SOP_MSG) {
usbpd_err(&pd->dev, "invalid msg type (%d) received; only SOP supported\n",
- type);
+ sop);
return;
}
@@ -964,7 +961,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->current_voltage = pd->requested_voltage = 5000000;
val.intval = pd->requested_voltage; /* set max range to 5V */
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, &val);
if (!pd->vbus_present) {
pd->current_state = PE_SNK_DISCOVERY;
@@ -1617,7 +1614,6 @@ static void usbpd_sm(struct work_struct *w)
else if (pd->current_dr == DR_DFP)
stop_usb_host(pd);
- pd->current_pr = PR_NONE;
pd->current_dr = DR_NONE;
reset_vdm_state(pd);
@@ -1663,7 +1659,7 @@ static void usbpd_sm(struct work_struct *w)
pd->requested_voltage = 5000000;
val.intval = pd->requested_voltage;
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MIN, &val);
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, &val);
pd->in_pr_swap = false;
val.intval = 0;
@@ -1982,8 +1978,8 @@ static void usbpd_sm(struct work_struct *w)
val.intval = pd->requested_voltage;
power_supply_set_property(pd->usb_psy,
pd->requested_voltage >= pd->current_voltage ?
- POWER_SUPPLY_PROP_VOLTAGE_MAX :
- POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MAX :
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MIN,
&val);
/*
@@ -2034,8 +2030,8 @@ static void usbpd_sm(struct work_struct *w)
val.intval = pd->requested_voltage;
power_supply_set_property(pd->usb_psy,
pd->requested_voltage >= pd->current_voltage ?
- POWER_SUPPLY_PROP_VOLTAGE_MIN :
- POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MIN :
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, &val);
pd->current_voltage = pd->requested_voltage;
/* resume charging */
@@ -2217,7 +2213,7 @@ static void usbpd_sm(struct work_struct *w)
val.intval = pd->requested_voltage;
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MIN, &val);
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, &val);
pd_send_hard_reset(pd);
pd->in_explicit_contract = false;
@@ -2480,6 +2476,16 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
if (pd->current_pr == PR_SINK)
return 0;
+ /*
+ * Unexpected if not in PR swap; need to force disconnect from
+ * source so we can turn off VBUS, Vconn, PD PHY etc.
+ */
+ if (pd->current_pr == PR_SRC) {
+ usbpd_info(&pd->dev, "Forcing disconnect from source mode\n");
+ pd->current_pr = PR_NONE;
+ break;
+ }
+
pd->current_pr = PR_SINK;
break;
diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c
index e200c25bc23a..b454442e2471 100644
--- a/drivers/usb/pd/qpnp-pdphy.c
+++ b/drivers/usb/pd/qpnp-pdphy.c
@@ -80,6 +80,10 @@
#define VDD_PDPHY_VOL_MAX 3300000 /* uV */
#define VDD_PDPHY_HPM_LOAD 3000 /* uA */
+/* timers */
+#define RECEIVER_RESPONSE_TIME 15 /* tReceiverResponse */
+#define HARD_RESET_COMPLETE_TIME 5 /* tHardResetComplete */
+
struct usb_pdphy {
struct device *dev;
struct regmap *regmap;
@@ -96,8 +100,8 @@ struct usb_pdphy {
int msg_tx_discarded_irq;
int msg_rx_discarded_irq;
- void (*signal_cb)(struct usbpd *pd, enum pd_sig_type type);
- void (*msg_rx_cb)(struct usbpd *pd, enum pd_msg_type type,
+ void (*signal_cb)(struct usbpd *pd, enum pd_sig_type sig);
+ void (*msg_rx_cb)(struct usbpd *pd, enum pd_sop_type sop,
u8 *buf, size_t len);
void (*shutdown_cb)(struct usbpd *pd);
@@ -401,14 +405,13 @@ int pd_phy_open(struct pd_phy_params *params)
}
EXPORT_SYMBOL(pd_phy_open);
-int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms)
+int pd_phy_signal(enum pd_sig_type sig)
{
u8 val;
int ret;
struct usb_pdphy *pdphy = __pdphy;
- dev_dbg(pdphy->dev, "%s: type %d timeout %u\n", __func__, type,
- timeout_ms);
+ dev_dbg(pdphy->dev, "%s: type %d\n", __func__, sig);
if (!pdphy) {
pr_err("%s: pdphy not found\n", __func__);
@@ -428,7 +431,7 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms)
usleep_range(2, 3);
- val = (type == CABLE_RESET_SIG ? TX_CONTROL_FRAME_TYPE_CABLE_RESET : 0)
+ val = (sig == CABLE_RESET_SIG ? TX_CONTROL_FRAME_TYPE_CABLE_RESET : 0)
| TX_CONTROL_SEND_SIGNAL;
ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, val);
@@ -436,7 +439,8 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms)
return ret;
ret = wait_event_interruptible_timeout(pdphy->tx_waitq,
- pdphy->tx_status != -EINPROGRESS, msecs_to_jiffies(timeout_ms));
+ pdphy->tx_status != -EINPROGRESS,
+ msecs_to_jiffies(HARD_RESET_COMPLETE_TIME));
if (ret <= 0) {
dev_err(pdphy->dev, "%s: failed ret %d", __func__, ret);
return ret ? ret : -ETIMEDOUT;
@@ -447,7 +451,7 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms)
if (pdphy->tx_status)
return pdphy->tx_status;
- if (type == HARD_RESET_SIG)
+ if (sig == HARD_RESET_SIG)
/* Frame filter is reconfigured in pd_phy_open() */
return pdphy_reg_write(pdphy, USB_PDPHY_FRAME_FILTER, 0);
@@ -455,16 +459,15 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms)
}
EXPORT_SYMBOL(pd_phy_signal);
-int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
- enum pd_msg_type type, unsigned int timeout_ms)
+int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, enum pd_sop_type sop)
{
u8 val;
int ret;
size_t total_len = data_len + USB_PDPHY_MSG_HDR_LEN;
struct usb_pdphy *pdphy = __pdphy;
- dev_dbg(pdphy->dev, "%s: hdr %x frame type %d timeout %u\n",
- __func__, hdr, type, timeout_ms);
+ dev_dbg(pdphy->dev, "%s: hdr %x frame sop_type %d\n",
+ __func__, hdr, sop);
if (data && data_len)
print_hex_dump_debug("tx data obj:", DUMP_PREFIX_NONE, 32, 4,
@@ -518,14 +521,15 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
usleep_range(2, 3);
- val = TX_CONTROL_RETRY_COUNT | (type << 2) | TX_CONTROL_SEND_MSG;
+ val = TX_CONTROL_RETRY_COUNT | (sop << 2) | TX_CONTROL_SEND_MSG;
ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, val);
if (ret)
return ret;
ret = wait_event_interruptible_timeout(pdphy->tx_waitq,
- pdphy->tx_status != -EINPROGRESS, msecs_to_jiffies(timeout_ms));
+ pdphy->tx_status != -EINPROGRESS,
+ msecs_to_jiffies(RECEIVER_RESPONSE_TIME));
if (ret <= 0) {
dev_err(pdphy->dev, "%s: failed ret %d", __func__, ret);
return ret ? ret : -ETIMEDOUT;
@@ -534,7 +538,7 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
if (hdr && !pdphy->tx_status)
pdphy->tx_bytes += data_len + USB_PDPHY_MSG_HDR_LEN;
- return pdphy->tx_status ? pdphy->tx_status : data_len;
+ return pdphy->tx_status ? pdphy->tx_status : 0;
}
EXPORT_SYMBOL(pd_phy_write);
diff --git a/drivers/usb/pd/usbpd.h b/drivers/usb/pd/usbpd.h
index 108701739f89..9b6053e940e9 100644
--- a/drivers/usb/pd/usbpd.h
+++ b/drivers/usb/pd/usbpd.h
@@ -45,7 +45,7 @@ enum pd_sig_type {
CABLE_RESET_SIG,
};
-enum pd_msg_type {
+enum pd_sop_type {
SOP_MSG = 0,
SOPI_MSG,
SOPII_MSG,
@@ -61,8 +61,8 @@ enum pd_spec_rev {
#define FRAME_FILTER_EN_HARD_RESET BIT(5)
struct pd_phy_params {
- void (*signal_cb)(struct usbpd *pd, enum pd_sig_type type);
- void (*msg_rx_cb)(struct usbpd *pd, enum pd_msg_type type,
+ void (*signal_cb)(struct usbpd *pd, enum pd_sig_type sig);
+ void (*msg_rx_cb)(struct usbpd *pd, enum pd_sop_type sop,
u8 *buf, size_t len);
void (*shutdown_cb)(struct usbpd *pd);
enum data_role data_role;
@@ -72,9 +72,9 @@ struct pd_phy_params {
#if IS_ENABLED(CONFIG_QPNP_USB_PDPHY)
int pd_phy_open(struct pd_phy_params *params);
-int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms);
+int pd_phy_signal(enum pd_sig_type sig);
int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
- enum pd_msg_type type, unsigned int timeout_ms);
+ enum pd_sop_type sop);
int pd_phy_update_roles(enum data_role dr, enum power_role pr);
void pd_phy_close(void);
#else
@@ -83,13 +83,13 @@ static inline int pd_phy_open(struct pd_phy_params *params)
return -ENODEV;
}
-static inline int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms)
+static inline int pd_phy_signal(enum pd_sig_type type)
{
return -ENODEV;
}
static inline int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
- enum pd_msg_type type, unsigned int timeout_ms)
+ enum pd_sop_type sop)
{
return -ENODEV;
}
diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c
index bd2722e8fc48..be63c6c0a86a 100644
--- a/drivers/usb/phy/phy-msm-qusb.c
+++ b/drivers/usb/phy/phy-msm-qusb.c
@@ -74,10 +74,6 @@
#define QUSB2PHY_PORT_TUNE4 0x8C
#define QUSB2PHY_PORT_TUNE5 0x90
-/* In case Efuse register shows zero, use this value */
-#define TUNE2_DEFAULT_HIGH_NIBBLE 0xB
-#define TUNE2_DEFAULT_LOW_NIBBLE 0x3
-
/* Get TUNE2's high nibble value read from efuse */
#define TUNE2_HIGH_NIBBLE_VAL(val, pos, mask) ((val >> pos) & mask)
@@ -147,6 +143,7 @@ struct qusb_phy {
u32 tune2_val;
int tune2_efuse_bit_pos;
int tune2_efuse_num_of_bits;
+ int tune2_efuse_correction;
bool power_enabled;
bool clocks_enabled;
@@ -433,6 +430,7 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy)
{
u8 num_of_bits;
u32 bit_mask = 1;
+ u8 reg_val;
pr_debug("%s(): num_of_bits:%d bit_pos:%d\n", __func__,
qphy->tune2_efuse_num_of_bits,
@@ -446,9 +444,8 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy)
/*
* Read EFUSE register having TUNE2 parameter's high nibble.
- * If efuse register shows value as 0x0, then use default value
- * as 0xB as high nibble. Otherwise use efuse register based
- * value for this purpose.
+ * If efuse register shows value as 0x0, then use previous value
+ * as it is. Otherwise use efuse register based value for this purpose.
*/
qphy->tune2_val = readl_relaxed(qphy->tune2_efuse_reg);
pr_debug("%s(): bit_mask:%d efuse based tune2 value:%d\n",
@@ -457,12 +454,24 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy)
qphy->tune2_val = TUNE2_HIGH_NIBBLE_VAL(qphy->tune2_val,
qphy->tune2_efuse_bit_pos, bit_mask);
- if (!qphy->tune2_val)
- qphy->tune2_val = TUNE2_DEFAULT_HIGH_NIBBLE;
+ /* Update higher nibble of TUNE2 value for better rise/fall times */
+ if (qphy->tune2_efuse_correction && qphy->tune2_val) {
+ if (qphy->tune2_efuse_correction > 5 ||
+ qphy->tune2_efuse_correction < -10)
+ pr_warn("Correction value is out of range : %d\n",
+ qphy->tune2_efuse_correction);
+ else
+ qphy->tune2_val = qphy->tune2_val +
+ qphy->tune2_efuse_correction;
+ }
+
+ reg_val = readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE2);
+ if (qphy->tune2_val) {
+ reg_val &= 0x0f;
+ reg_val |= (qphy->tune2_val << 4);
+ }
- /* Get TUNE2 byte value using high and low nibble value */
- qphy->tune2_val = ((qphy->tune2_val << 0x4) |
- TUNE2_DEFAULT_LOW_NIBBLE);
+ qphy->tune2_val = reg_val;
}
static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt,
@@ -570,7 +579,7 @@ static int qusb_phy_init(struct usb_phy *phy)
* and try to read EFUSE value only once i.e. not every USB
* cable connect case.
*/
- if (qphy->tune2_efuse_reg) {
+ if (qphy->tune2_efuse_reg && !tune2) {
if (!qphy->tune2_val)
qusb_phy_get_tune2_param(qphy);
@@ -929,6 +938,9 @@ static int qusb_phy_probe(struct platform_device *pdev)
"qcom,tune2-efuse-num-bits",
&qphy->tune2_efuse_num_of_bits);
}
+ of_property_read_u32(dev->of_node,
+ "qcom,tune2-efuse-correction",
+ &qphy->tune2_efuse_correction);
if (ret) {
dev_err(dev, "DT Value for tune2 efuse is invalid.\n");
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index 6ba742076baa..2bc3c6fa417a 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -149,15 +149,17 @@ static void msm_ssusb_qmp_enable_autonomous(struct msm_ssphy_qmp *phy,
if (enable) {
msm_ssusb_qmp_clr_lfps_rxterm_int(phy);
+ val = readb_relaxed(phy->base + autonomous_mode_offset);
+ val |= ARCVR_DTCT_EN;
if (phy->phy.flags & DEVICE_IN_SS_MODE) {
- val =
- readb_relaxed(phy->base + autonomous_mode_offset);
- val |= ARCVR_DTCT_EN;
val |= ALFPS_DTCT_EN;
val &= ~ARCVR_DTCT_EVENT_SEL;
- writeb_relaxed(val, phy->base + autonomous_mode_offset);
+ } else {
+ val &= ~ALFPS_DTCT_EN;
+ val |= ARCVR_DTCT_EVENT_SEL;
}
+ writeb_relaxed(val, phy->base + autonomous_mode_offset);
/* clamp phy level shifter to perform autonomous detection */
writel_relaxed(0x1, phy->vls_clamp_reg);
} else {
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index d82fa36c3465..005da0866836 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -733,8 +733,10 @@ static int usbhsc_resume(struct device *dev)
struct usbhs_priv *priv = dev_get_drvdata(dev);
struct platform_device *pdev = usbhs_priv_to_pdev(priv);
- if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+ if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) {
usbhsc_power_ctrl(priv, 1);
+ usbhs_mod_autonomy_mode(priv);
+ }
usbhs_platform_call(priv, phy_reset, pdev);
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index efc4fae123a4..8647d2c2a8c4 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -37,6 +37,7 @@ struct usbhsg_gpriv;
struct usbhsg_uep {
struct usb_ep ep;
struct usbhs_pipe *pipe;
+ spinlock_t lock; /* protect the pipe */
char ep_name[EP_NAME_SIZE];
@@ -638,10 +639,16 @@ usbhsg_ep_enable_end:
static int usbhsg_ep_disable(struct usb_ep *ep)
{
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhs_pipe *pipe;
+ unsigned long flags;
+ int ret = 0;
- if (!pipe)
- return -EINVAL;
+ spin_lock_irqsave(&uep->lock, flags);
+ pipe = usbhsg_uep_to_pipe(uep);
+ if (!pipe) {
+ ret = -EINVAL;
+ goto out;
+ }
usbhsg_pipe_disable(uep);
usbhs_pipe_free(pipe);
@@ -649,6 +656,9 @@ static int usbhsg_ep_disable(struct usb_ep *ep)
uep->pipe->mod_private = NULL;
uep->pipe = NULL;
+out:
+ spin_unlock_irqrestore(&uep->lock, flags);
+
return 0;
}
@@ -698,8 +708,11 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhs_pipe *pipe;
+ unsigned long flags;
+ spin_lock_irqsave(&uep->lock, flags);
+ pipe = usbhsg_uep_to_pipe(uep);
if (pipe)
usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
@@ -708,6 +721,7 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
* even if the pipe is NULL.
*/
usbhsg_queue_pop(uep, ureq, -ECONNRESET);
+ spin_unlock_irqrestore(&uep->lock, flags);
return 0;
}
@@ -854,10 +868,10 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
{
struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
- struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+ struct usbhsg_uep *uep;
struct device *dev = usbhs_priv_to_dev(priv);
unsigned long flags;
- int ret = 0;
+ int ret = 0, i;
/******************** spin lock ********************/
usbhs_lock(priv, flags);
@@ -889,7 +903,9 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
usbhs_sys_set_test_mode(priv, 0);
usbhs_sys_function_ctrl(priv, 0);
- usbhsg_ep_disable(&dcp->ep);
+ /* disable all eps */
+ usbhsg_for_each_uep_with_dcp(uep, gpriv, i)
+ usbhsg_ep_disable(&uep->ep);
dev_dbg(dev, "stop gadget\n");
@@ -1072,6 +1088,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
ret = -ENOMEM;
goto usbhs_mod_gadget_probe_err_gpriv;
}
+ spin_lock_init(&uep->lock);
gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
dev_info(dev, "%stransceiver found\n",
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 33cec50978b8..41a6513646de 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -134,6 +134,8 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */
{ USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */
{ USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */
+ { USB_DEVICE(0x10C4, 0x8A5E) }, /* CEL EM3588 ZigBee USB Stick Long Range */
+ { USB_DEVICE(0x10C4, 0x8B34) }, /* Qivicon ZigBee USB Radio Stick */
{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 3bf61acfc26b..fe123153b1a5 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1877,6 +1877,10 @@ static const struct usb_device_id option_ids[] = {
.driver_info = (kernel_ulong_t)&four_g_w100_blacklist
},
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) },
+ { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9801, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9803, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) },
{ USB_DEVICE(HAIER_VENDOR_ID, HAIER_PRODUCT_CE100) },
@@ -2021,6 +2025,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff), /* D-Link DWM-222 */
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 1db4b61bdf7b..a51b28379850 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -49,6 +49,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
+ { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC485) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) },
{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 09d9be88209e..3b5a15d1dc0d 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -27,6 +27,7 @@
#define ATEN_VENDOR_ID 0x0557
#define ATEN_VENDOR_ID2 0x0547
#define ATEN_PRODUCT_ID 0x2008
+#define ATEN_PRODUCT_UC485 0x2021
#define ATEN_PRODUCT_ID2 0x2118
#define IODATA_VENDOR_ID 0x04bb
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index fd509ed6cf70..652b4334b26d 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -158,6 +158,7 @@ static const struct usb_device_id id_table[] = {
{DEVICE_SWI(0x1199, 0x9056)}, /* Sierra Wireless Modem */
{DEVICE_SWI(0x1199, 0x9060)}, /* Sierra Wireless Modem */
{DEVICE_SWI(0x1199, 0x9061)}, /* Sierra Wireless Modem */
+ {DEVICE_SWI(0x1199, 0x9063)}, /* Sierra Wireless EM7305 */
{DEVICE_SWI(0x1199, 0x9070)}, /* Sierra Wireless MC74xx */
{DEVICE_SWI(0x1199, 0x9071)}, /* Sierra Wireless MC74xx */
{DEVICE_SWI(0x1199, 0x9078)}, /* Sierra Wireless EM74xx */
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 39afd7045c43..7bb5f8da5357 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1520,8 +1520,11 @@ static void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us)
/* Make sure driver was initialized */
- if (us->extra == NULL)
+ if (us->extra == NULL) {
usb_stor_dbg(us, "ERROR Driver not initialized\n");
+ srb->result = DID_ERROR << 16;
+ return;
+ }
scsi_set_resid(srb, 0);
/* scsi_bufflen might change in protocol translation to ata */
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index 53341a77d89f..a37ed1e59e99 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -123,9 +123,9 @@ UNUSUAL_DEV(0x0bc2, 0xab2a, 0x0000, 0x9999,
/* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */
UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999,
"Initio Corporation",
- "",
+ "INIC-3069",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
- US_FL_NO_ATA_1X),
+ US_FL_NO_ATA_1X | US_FL_IGNORE_RESIDUE),
/* Reported-by: Tom Arild Naess <tanaess@gmail.com> */
UNUSUAL_DEV(0x152d, 0x0539, 0x0000, 0x9999,
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index 44ab43fc4fcc..af10f7b131a4 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -262,7 +262,11 @@ void stub_device_cleanup_urbs(struct stub_device *sdev)
kmem_cache_free(stub_priv_cache, priv);
kfree(urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+
kfree(urb->setup_packet);
+ urb->setup_packet = NULL;
+
usb_free_urb(urb);
}
}
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index dbcabc9dbe0d..021003c4de53 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -28,7 +28,11 @@ static void stub_free_priv_and_urb(struct stub_priv *priv)
struct urb *urb = priv->urb;
kfree(urb->setup_packet);
+ urb->setup_packet = NULL;
+
kfree(urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+
list_del(&priv->list);
kmem_cache_free(stub_priv_cache, priv);
usb_free_urb(urb);
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 830e2fd47642..b31b84f56e8f 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -902,6 +902,10 @@ static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
return ret;
vdev->barmap[index] = pci_iomap(pdev, index, 0);
+ if (!vdev->barmap[index]) {
+ pci_release_selected_regions(pdev, 1 << index);
+ return -ENOMEM;
+ }
}
vma->vm_private_data = vdev;
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index 210db24d2204..4d39f7959adf 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -190,7 +190,10 @@ ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
if (!vdev->has_vga)
return -EINVAL;
- switch (pos) {
+ if (pos > 0xbfffful)
+ return -EINVAL;
+
+ switch ((u32)pos) {
case 0xa0000 ... 0xbffff:
count = min(count, (size_t)(0xc0000 - pos));
iomem = ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1);
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 6070b793cbcb..1e01e28f40f3 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -296,6 +296,34 @@ static void vfio_group_put(struct vfio_group *group)
kref_put_mutex(&group->kref, vfio_group_release, &vfio.group_lock);
}
+struct vfio_group_put_work {
+ struct work_struct work;
+ struct vfio_group *group;
+};
+
+static void vfio_group_put_bg(struct work_struct *work)
+{
+ struct vfio_group_put_work *do_work;
+
+ do_work = container_of(work, struct vfio_group_put_work, work);
+
+ vfio_group_put(do_work->group);
+ kfree(do_work);
+}
+
+static void vfio_group_schedule_put(struct vfio_group *group)
+{
+ struct vfio_group_put_work *do_work;
+
+ do_work = kmalloc(sizeof(*do_work), GFP_KERNEL);
+ if (WARN_ON(!do_work))
+ return;
+
+ INIT_WORK(&do_work->work, vfio_group_put_bg);
+ do_work->group = group;
+ schedule_work(&do_work->work);
+}
+
/* Assume group_lock or group reference is held */
static void vfio_group_get(struct vfio_group *group)
{
@@ -620,7 +648,14 @@ static int vfio_iommu_group_notifier(struct notifier_block *nb,
break;
}
- vfio_group_put(group);
+ /*
+ * If we're the last reference to the group, the group will be
+ * released, which includes unregistering the iommu group notifier.
+ * We hold a read-lock on that notifier list, unregistering needs
+ * a write-lock... deadlock. Release our reference asynchronously
+ * to avoid that situation.
+ */
+ vfio_group_schedule_put(group);
return NOTIFY_OK;
}
@@ -1552,6 +1587,15 @@ void vfio_group_put_external_user(struct vfio_group *group)
}
EXPORT_SYMBOL_GPL(vfio_group_put_external_user);
+bool vfio_external_group_match_file(struct vfio_group *test_group,
+ struct file *filep)
+{
+ struct vfio_group *group = filep->private_data;
+
+ return (filep->f_op == &vfio_group_fops) && (group == test_group);
+}
+EXPORT_SYMBOL_GPL(vfio_external_group_match_file);
+
int vfio_external_user_iommu_id(struct vfio_group *group)
{
return iommu_group_id(group->iommu_group);
diff --git a/drivers/video/fbdev/cobalt_lcdfb.c b/drivers/video/fbdev/cobalt_lcdfb.c
index 07675d6f323e..d4530b54479c 100644
--- a/drivers/video/fbdev/cobalt_lcdfb.c
+++ b/drivers/video/fbdev/cobalt_lcdfb.c
@@ -350,6 +350,11 @@ static int cobalt_lcdfb_probe(struct platform_device *dev)
info->screen_size = resource_size(res);
info->screen_base = devm_ioremap(&dev->dev, res->start,
info->screen_size);
+ if (!info->screen_base) {
+ framebuffer_release(info);
+ return -ENOMEM;
+ }
+
info->fbops = &cobalt_lcd_fbops;
info->fix = cobalt_lcdfb_fix;
info->fix.smem_start = res->start;
diff --git a/drivers/video/fbdev/msm/mdss_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 23a63121b78c..c0632e8241a0 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -395,6 +395,7 @@ static int dp_aux_rw_cmds_retry(struct mdss_dp_drv_pdata *dp,
int i;
u32 aux_cfg1_config_count;
int ret;
+ bool connected = false;
aux_cfg1_config_count = mdss_dp_phy_aux_get_config_cnt(dp,
PHY_AUX_CFG1);
@@ -404,6 +405,15 @@ retry:
do {
struct edp_cmd cmd1 = *cmd;
+ mutex_lock(&dp->attention_lock);
+ connected = dp->cable_connected;
+ mutex_unlock(&dp->attention_lock);
+
+ if (!connected) {
+ pr_err("dp cable disconnected\n");
+ break;
+ }
+
dp->aux_error_num = EDP_AUX_ERR_NONE;
pr_debug("Trying %s, iteration count: %d\n",
mdss_dp_aux_transaction_to_string(transaction),
@@ -674,6 +684,11 @@ char mdss_dp_gen_link_clk(struct mdss_dp_drv_pdata *dp)
pr_debug("clk_rate=%llu, bpp= %d, lane_cnt=%d\n",
pinfo->clk_rate, pinfo->bpp, lane_cnt);
+ if (lane_cnt == 0) {
+ pr_warn("Invalid max lane count\n");
+ return 0;
+ }
+
/*
* The max pixel clock supported is 675Mhz. The
* current calculations below will make sure
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 4ac10ab494e5..5f7e7c6bcde0 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -2720,8 +2720,6 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
ctrl_pdata->update_phy_timing);
rc = mdss_dsi_on(pdata);
- mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode,
- pdata);
break;
case MDSS_EVENT_UNBLANK:
if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE)
@@ -3268,21 +3266,6 @@ end:
return rc;
}
-static void mdss_dsi_ctrl_validate_lane_swap_config(
- struct mdss_dsi_ctrl_pdata *ctrl)
-{
- struct mipi_panel_info *mipi = &ctrl->panel_data.panel_info.mipi;
-
- if (!mipi->data_lane0)
- ctrl->lane_map[DSI_LOGICAL_LANE_0] = DSI_PHYSICAL_LANE_INVALID;
- if (!mipi->data_lane1)
- ctrl->lane_map[DSI_LOGICAL_LANE_1] = DSI_PHYSICAL_LANE_INVALID;
- if (!mipi->data_lane2)
- ctrl->lane_map[DSI_LOGICAL_LANE_2] = DSI_PHYSICAL_LANE_INVALID;
- if (!mipi->data_lane3)
- ctrl->lane_map[DSI_LOGICAL_LANE_3] = DSI_PHYSICAL_LANE_INVALID;
-}
-
static int mdss_dsi_ctrl_validate_config(struct mdss_dsi_ctrl_pdata *ctrl)
{
int rc = 0;
@@ -3292,8 +3275,6 @@ static int mdss_dsi_ctrl_validate_config(struct mdss_dsi_ctrl_pdata *ctrl)
goto error;
}
- mdss_dsi_ctrl_validate_lane_swap_config(ctrl);
-
/*
* check to make sure that the byte interface clock is specified for
* DSI ctrl version 2 and above.
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index abd2192997e5..a49c5290753c 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -156,10 +156,13 @@ struct hdmi_edid_ctrl {
};
static bool hdmi_edid_is_mode_supported(struct hdmi_edid_ctrl *edid_ctrl,
- struct msm_hdmi_mode_timing_info *timing)
+ struct msm_hdmi_mode_timing_info *timing, u32 out_format)
{
+ u32 pclk = hdmi_tx_setup_tmds_clk_rate(timing->pixel_freq,
+ out_format, false);
+
if (!timing->supported ||
- timing->pixel_freq > edid_ctrl->init_data.max_pclk_khz)
+ pclk > edid_ctrl->init_data.max_pclk_khz)
return false;
return true;
@@ -936,7 +939,8 @@ static void hdmi_edid_add_sink_y420_format(struct hdmi_edid_ctrl *edid_ctrl,
u32 ret = hdmi_get_supported_mode(&timing,
&edid_ctrl->init_data.ds_data,
video_format);
- u32 supported = hdmi_edid_is_mode_supported(edid_ctrl, &timing);
+ u32 supported = hdmi_edid_is_mode_supported(edid_ctrl,
+ &timing, MDP_Y_CBCR_H2V2);
struct hdmi_edid_sink_data *sink = &edid_ctrl->sink_data;
if (video_format >= HDMI_VFRMT_MAX) {
@@ -1704,7 +1708,8 @@ static void hdmi_edid_add_sink_video_format(struct hdmi_edid_ctrl *edid_ctrl,
u32 ret = hdmi_get_supported_mode(&timing,
&edid_ctrl->init_data.ds_data,
video_format);
- u32 supported = hdmi_edid_is_mode_supported(edid_ctrl, &timing);
+ u32 supported = hdmi_edid_is_mode_supported(edid_ctrl,
+ &timing, MDP_RGBA_8888);
struct hdmi_edid_sink_data *sink_data = &edid_ctrl->sink_data;
struct disp_mode_info *disp_mode_list = sink_data->disp_mode_list;
u32 i = 0;
@@ -2317,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_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index 9ee0c27b225e..837147dc5036 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -272,20 +272,6 @@ static void hdmi_tx_cable_notify_work(struct work_struct *work)
mutex_unlock(&hdmi_ctrl->tx_lock);
} /* hdmi_tx_cable_notify_work */
-static bool hdmi_tx_is_cea_format(int mode)
-{
- bool cea_fmt;
-
- if ((mode > 0) && (mode <= HDMI_EVFRMT_END))
- cea_fmt = true;
- else
- cea_fmt = false;
-
- DEV_DBG("%s: %s\n", __func__, cea_fmt ? "Yes" : "No");
-
- return cea_fmt;
-}
-
static inline bool hdmi_tx_is_hdcp_enabled(struct hdmi_tx_ctrl *hdmi_ctrl)
{
return hdmi_ctrl->hdcp_feature_on &&
@@ -2193,6 +2179,7 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl)
int status = 0;
void *data;
struct dss_io_data *io;
+ u32 sink_max_pclk;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -2232,16 +2219,22 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl)
/* parse edid if a valid edid buffer is present */
if (hdmi_ctrl->custom_edid || !hdmi_ctrl->sim_mode) {
status = hdmi_edid_parser(data);
- if (status)
+ if (status) {
DEV_ERR("%s: edid parse failed\n", __func__);
- else
+ } else {
/*
- * Updata HDMI max supported TMDS clock, consider
- * both sink and source capicity.
+ * Update HDMI max supported TMDS clock, consider
+ * both sink and source capacity. For DVI sink,
+ * could not get max TMDS clock from EDID, so just
+ * use source capacity.
*/
- hdmi_edid_set_max_pclk_rate(data,
- min(hdmi_edid_get_sink_caps_max_tmds_clk(data) / 1000,
- hdmi_ctrl->max_pclk_khz));
+ sink_max_pclk =
+ hdmi_edid_get_sink_caps_max_tmds_clk(data);
+ if (sink_max_pclk != 0)
+ hdmi_edid_set_max_pclk_rate(data,
+ min(sink_max_pclk / 1000,
+ hdmi_ctrl->max_pclk_khz));
+ }
}
bail:
if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, false))
@@ -3192,9 +3185,7 @@ static int hdmi_tx_power_on(struct hdmi_tx_ctrl *hdmi_ctrl)
}
hdmi_ctrl->panel.vic = hdmi_ctrl->vic;
-
- if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) &&
- hdmi_tx_is_cea_format(hdmi_ctrl->vic))
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl))
hdmi_ctrl->panel.infoframe = true;
else
hdmi_ctrl->panel.infoframe = false;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 6fb32761a767..410d36a3ac31 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1797,7 +1797,8 @@ static inline int mdss_mdp_irq_clk_register(struct mdss_data_type *mdata,
static void __mdss_restore_sec_cfg(struct mdss_data_type *mdata)
{
- int ret, scm_ret = 0;
+ int ret;
+ u64 scm_ret = 0;
if (test_bit(MDSS_CAPS_SCM_RESTORE_NOT_REQUIRED, mdata->mdss_caps_map))
return;
@@ -1808,7 +1809,7 @@ static void __mdss_restore_sec_cfg(struct mdss_data_type *mdata)
ret = scm_restore_sec_cfg(SEC_DEVICE_MDSS, 0, &scm_ret);
if (ret || scm_ret)
- pr_warn("scm_restore_sec_cfg failed %d %d\n",
+ pr_warn("scm_restore_sec_cfg failed %d %llu\n",
ret, scm_ret);
__mdss_mdp_reg_access_clk_enable(mdata, false);
@@ -2779,6 +2780,8 @@ ssize_t mdss_mdp_show_capabilities(struct device *dev,
SPRINT(" avr");
if (mdss_has_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED))
SPRINT(" hdr");
+ if (mdata->nvig_pipes && mdata->mdp_rev >= MDSS_MDP_HW_REV_300)
+ SPRINT(" vig_csc_db"); /* double buffered VIG CSC block */
SPRINT("\n");
#undef SPRINT
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 0165c48a0467..54fb21a5f35d 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -696,7 +696,6 @@ static u32 get_pipe_mdp_clk_rate(struct mdss_mdp_pipe *pipe,
if (flags & PERF_CALC_PIPE_APPLY_CLK_FUDGE)
rate = mdss_mdp_clk_fudge_factor(mixer, rate);
- rate = min(mdata->max_mdp_clk_rate, rate);
return rate;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c
index 1035d23fe9ce..09d1dab0d180 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_debug.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c
@@ -1863,12 +1863,14 @@ static void __print_buf(struct seq_file *s, struct mdss_mdp_data *buf,
seq_puts(s, "\n");
}
-static void __dump_pipe(struct seq_file *s, struct mdss_mdp_pipe *pipe)
+static void __dump_pipe(struct seq_file *s, struct mdss_mdp_pipe *pipe,
+ struct msm_fb_data_type *mfd)
{
struct mdss_mdp_data *buf;
int format;
int smps[4];
int i;
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
seq_printf(s, "\nSSPP #%d type=%s ndx=%x flags=0x%16llx play_cnt=%u xin_id=%d\n",
pipe->num, mdss_mdp_pipetype2str(pipe->type),
@@ -1923,11 +1925,14 @@ static void __dump_pipe(struct seq_file *s, struct mdss_mdp_pipe *pipe)
seq_puts(s, "Data:\n");
+ mutex_lock(&mdp5_data->list_lock);
list_for_each_entry(buf, &pipe->buf_queue, pipe_list)
__print_buf(s, buf, false);
+ mutex_unlock(&mdp5_data->list_lock);
}
-static void __dump_mixer(struct seq_file *s, struct mdss_mdp_mixer *mixer)
+static void __dump_mixer(struct seq_file *s, struct mdss_mdp_mixer *mixer,
+ struct msm_fb_data_type *mfd)
{
struct mdss_mdp_pipe *pipe;
int i, cnt = 0;
@@ -1944,7 +1949,7 @@ static void __dump_mixer(struct seq_file *s, struct mdss_mdp_mixer *mixer)
for (i = 0; i < ARRAY_SIZE(mixer->stage_pipe); i++) {
pipe = mixer->stage_pipe[i];
if (pipe) {
- __dump_pipe(s, pipe);
+ __dump_pipe(s, pipe, mfd);
cnt++;
}
}
@@ -2019,8 +2024,8 @@ static void __dump_ctl(struct seq_file *s, struct mdss_mdp_ctl *ctl)
seq_printf(s, "Play Count=%u Underrun Count=%u\n",
ctl->play_cnt, ctl->underrun_cnt);
- __dump_mixer(s, ctl->mixer_left);
- __dump_mixer(s, ctl->mixer_right);
+ __dump_mixer(s, ctl->mixer_left, ctl->mfd);
+ __dump_mixer(s, ctl->mixer_right, ctl->mfd);
}
static int __dump_mdp(struct seq_file *s, struct mdss_data_type *mdata)
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index c31c0149866a..bdf6705ef597 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -643,8 +643,13 @@ static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl,
display_hctl = (hsync_end_x << 16) | hsync_start_x;
den_polarity = 0;
- hsync_polarity = p->h_polarity;
- vsync_polarity = p->v_polarity;
+ if (ctx->intf_type == MDSS_INTF_HDMI) {
+ hsync_polarity = p->h_polarity;
+ vsync_polarity = p->v_polarity;
+ } else {
+ hsync_polarity = 0;
+ vsync_polarity = 0;
+ }
polarity_ctl = (den_polarity << 2) | /* DEN Polarity */
(vsync_polarity << 1) | /* VSYNC Polarity */
(hsync_polarity << 0); /* HSYNC Polarity */
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index 3ccc09d58480..b07ba82fde34 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -975,26 +975,31 @@ static int __validate_layer_reconfig(struct mdp_input_layer *layer,
struct mdss_mdp_pipe *pipe)
{
int status = 0;
- struct mdss_mdp_format_params *src_fmt;
+ struct mdss_mdp_format_params *layer_src_fmt;
+ struct mdss_data_type *mdata = mfd_to_mdata(pipe->mfd);
+ bool is_csc_db = (mdata->mdp_rev < MDSS_MDP_HW_REV_300) ? false : true;
+
+ layer_src_fmt = mdss_mdp_get_format_params(layer->buffer.format);
+ if (!layer_src_fmt) {
+ pr_err("Invalid layer format %d\n", layer->buffer.format);
+ status = -EINVAL;
+ goto err_exit;
+ }
/*
- * csc registers are not double buffered. It is not permitted
- * to change them on staged pipe with YUV layer.
+ * HW earlier to sdm 3.x.x does not support double buffer CSC.
+ * Invalidate any reconfig of CSC block on staged pipe.
*/
- if (pipe->csc_coeff_set != layer->color_space) {
- src_fmt = mdss_mdp_get_format_params(layer->buffer.format);
- if (!src_fmt) {
- pr_err("Invalid layer format %d\n",
- layer->buffer.format);
- status = -EINVAL;
- } else {
- if (pipe->src_fmt->is_yuv && src_fmt &&
- src_fmt->is_yuv) {
- status = -EPERM;
- pr_err("csc change is not permitted on used pipe\n");
- }
- }
+ if (!is_csc_db &&
+ ((!!pipe->src_fmt->is_yuv != !!layer_src_fmt->is_yuv) ||
+ (pipe->src_fmt->is_yuv && layer_src_fmt->is_yuv &&
+ pipe->csc_coeff_set != layer->color_space))) {
+ pr_err("CSC reconfig not allowed on staged pipe\n");
+ status = -EINVAL;
+ goto err_exit;
}
+
+err_exit:
return status;
}
@@ -3186,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/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index 61b0518d3ee6..2028222748c3 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -373,6 +373,15 @@ static bool mdss_rotator_is_work_pending(struct mdss_rot_mgr *mgr,
return false;
}
+static void mdss_rotator_install_fence_fd(struct mdss_rot_entry_container *req)
+{
+ int i = 0;
+
+ for (i = 0; i < req->count; i++)
+ sync_fence_install(req->entries[i].output_fence,
+ req->entries[i].output_fence_fd);
+}
+
static int mdss_rotator_create_fence(struct mdss_rot_entry *entry)
{
int ret = 0, fd;
@@ -411,7 +420,6 @@ static int mdss_rotator_create_fence(struct mdss_rot_entry *entry)
goto get_fd_err;
}
- sync_fence_install(fence, fd);
rot_timeline->next_value++;
mutex_unlock(&rot_timeline->lock);
@@ -2240,6 +2248,7 @@ static int mdss_rotator_handle_request(struct mdss_rot_mgr *mgr,
goto handle_request_err1;
}
+ mdss_rotator_install_fence_fd(req);
mdss_rotator_queue_request(mgr, private, req);
mutex_unlock(&mgr->lock);
@@ -2400,6 +2409,7 @@ static int mdss_rotator_handle_request32(struct mdss_rot_mgr *mgr,
goto handle_request32_err1;
}
+ mdss_rotator_install_fence_fd(req);
mdss_rotator_queue_request(mgr, private, req);
mutex_unlock(&mgr->lock);
diff --git a/drivers/xen/biomerge.c b/drivers/xen/biomerge.c
index 4da69dbf7dca..1bdd02a6d6ac 100644
--- a/drivers/xen/biomerge.c
+++ b/drivers/xen/biomerge.c
@@ -10,8 +10,7 @@ bool xen_biovec_phys_mergeable(const struct bio_vec *vec1,
unsigned long bfn1 = pfn_to_bfn(page_to_pfn(vec1->bv_page));
unsigned long bfn2 = pfn_to_bfn(page_to_pfn(vec2->bv_page));
- return __BIOVEC_PHYS_MERGEABLE(vec1, vec2) &&
- ((bfn1 == bfn2) || ((bfn1+1) == bfn2));
+ return bfn1 + PFN_DOWN(vec1->bv_offset + vec1->bv_len) == bfn2;
#else
/*
* XXX: Add support for merging bio_vec when using different page
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 6c031dd1bc4e..8a0243efd359 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -905,17 +905,60 @@ static int load_elf_binary(struct linux_binprm *bprm)
elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
vaddr = elf_ppnt->p_vaddr;
+ /*
+ * If we are loading ET_EXEC or we have already performed
+ * the ET_DYN load_addr calculations, proceed normally.
+ */
if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
elf_flags |= MAP_FIXED;
} else if (loc->elf_ex.e_type == ET_DYN) {
- /* Try and get dynamic programs out of the way of the
- * default mmap base, as well as whatever program they
- * might try to exec. This is because the brk will
- * follow the loader, and is not movable. */
- load_bias = ELF_ET_DYN_BASE - vaddr;
- if (current->flags & PF_RANDOMIZE)
- load_bias += arch_mmap_rnd();
- load_bias = ELF_PAGESTART(load_bias);
+ /*
+ * This logic is run once for the first LOAD Program
+ * Header for ET_DYN binaries to calculate the
+ * randomization (load_bias) for all the LOAD
+ * Program Headers, and to calculate the entire
+ * size of the ELF mapping (total_size). (Note that
+ * load_addr_set is set to true later once the
+ * initial mapping is performed.)
+ *
+ * There are effectively two types of ET_DYN
+ * binaries: programs (i.e. PIE: ET_DYN with INTERP)
+ * and loaders (ET_DYN without INTERP, since they
+ * _are_ the ELF interpreter). The loaders must
+ * be loaded away from programs since the program
+ * may otherwise collide with the loader (especially
+ * for ET_EXEC which does not have a randomized
+ * position). For example to handle invocations of
+ * "./ld.so someprog" to test out a new version of
+ * the loader, the subsequent program that the
+ * loader loads must avoid the loader itself, so
+ * they cannot share the same load range. Sufficient
+ * room for the brk must be allocated with the
+ * loader as well, since brk must be available with
+ * the loader.
+ *
+ * Therefore, programs are loaded offset from
+ * ELF_ET_DYN_BASE and loaders are loaded into the
+ * independently randomized mmap region (0 load_bias
+ * without MAP_FIXED).
+ */
+ if (elf_interpreter) {
+ load_bias = ELF_ET_DYN_BASE;
+ if (current->flags & PF_RANDOMIZE)
+ load_bias += arch_mmap_rnd();
+ elf_flags |= MAP_FIXED;
+ } else
+ load_bias = 0;
+
+ /*
+ * Since load_bias is used for all subsequent loading
+ * calculations, we must lower it by the first vaddr
+ * so that the remaining calculations based on the
+ * ELF vaddrs will be correctly offset. The result
+ * is then page aligned.
+ */
+ load_bias = ELF_PAGESTART(load_bias - vaddr);
+
total_size = total_mapping_size(elf_phdata,
loc->elf_ex.e_phnum);
if (!total_size) {
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index a61926cb01c0..bebd6517355d 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7521,11 +7521,18 @@ static void adjust_dio_outstanding_extents(struct inode *inode,
* within our reservation, otherwise we need to adjust our inode
* counter appropriately.
*/
- if (dio_data->outstanding_extents) {
+ if (dio_data->outstanding_extents >= num_extents) {
dio_data->outstanding_extents -= num_extents;
} else {
+ /*
+ * If dio write length has been split due to no large enough
+ * contiguous space, we need to compensate our inode counter
+ * appropriately.
+ */
+ u64 num_needed = num_extents - dio_data->outstanding_extents;
+
spin_lock(&BTRFS_I(inode)->lock);
- BTRFS_I(inode)->outstanding_extents += num_extents;
+ BTRFS_I(inode)->outstanding_extents += num_needed;
spin_unlock(&BTRFS_I(inode)->lock);
}
}
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 9314b4ea2375..be7d187d53fd 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -247,6 +247,11 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx,
if (ret < 0)
err = ret;
dput(last);
+ /* last_name no longer match cache index */
+ if (fi->readdir_cache_idx >= 0) {
+ fi->readdir_cache_idx = -1;
+ fi->dir_release_count = 0;
+ }
}
return err;
}
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 26a3b389a265..fa8df3fef6fc 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -183,15 +183,20 @@ cifs_bp_rename_retry:
}
/*
+ * Don't allow path components longer than the server max.
* Don't allow the separator character in a path component.
* The VFS will not allow "/", but "\" is allowed by posix.
*/
static int
-check_name(struct dentry *direntry)
+check_name(struct dentry *direntry, struct cifs_tcon *tcon)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
int i;
+ if (unlikely(direntry->d_name.len >
+ tcon->fsAttrInfo.MaxPathNameComponentLength))
+ return -ENAMETOOLONG;
+
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
for (i = 0; i < direntry->d_name.len; i++) {
if (direntry->d_name.name[i] == '\\') {
@@ -489,10 +494,6 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
return finish_no_open(file, res);
}
- rc = check_name(direntry);
- if (rc)
- return rc;
-
xid = get_xid();
cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
@@ -505,6 +506,11 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
}
tcon = tlink_tcon(tlink);
+
+ rc = check_name(direntry, tcon);
+ if (rc)
+ goto out_free_xid;
+
server = tcon->ses->server;
if (server->ops->new_lease_key)
@@ -765,7 +771,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
}
pTcon = tlink_tcon(tlink);
- rc = check_name(direntry);
+ rc = check_name(direntry, pTcon);
if (rc)
goto lookup_out;
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index f4afa3b1cc56..6c484ddf26a9 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -2768,8 +2768,8 @@ copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) *
le32_to_cpu(pfs_inf->SectorsPerAllocationUnit);
kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits);
- kst->f_bfree = le64_to_cpu(pfs_inf->ActualAvailableAllocationUnits);
- kst->f_bavail = le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
+ kst->f_bfree = kst->f_bavail =
+ le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
return;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index 4c07a84d1317..ba56a39a3b74 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -269,6 +269,33 @@ static inline int dname_external(const struct dentry *dentry)
return dentry->d_name.name != dentry->d_iname;
}
+void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
+{
+ spin_lock(&dentry->d_lock);
+ if (unlikely(dname_external(dentry))) {
+ struct external_name *p = external_name(dentry);
+ atomic_inc(&p->u.count);
+ spin_unlock(&dentry->d_lock);
+ name->name = p->name;
+ } else {
+ memcpy(name->inline_name, dentry->d_iname, DNAME_INLINE_LEN);
+ spin_unlock(&dentry->d_lock);
+ name->name = name->inline_name;
+ }
+}
+EXPORT_SYMBOL(take_dentry_name_snapshot);
+
+void release_dentry_name_snapshot(struct name_snapshot *name)
+{
+ if (unlikely(name->name != name->inline_name)) {
+ struct external_name *p;
+ p = container_of(name->name, struct external_name, name[0]);
+ if (unlikely(atomic_dec_and_test(&p->u.count)))
+ kfree_rcu(p, u.head);
+ }
+}
+EXPORT_SYMBOL(release_dentry_name_snapshot);
+
static inline void __d_set_inode_and_type(struct dentry *dentry,
struct inode *inode,
unsigned type_flags)
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 0f5d05bf2131..e49ba072bd64 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -669,7 +669,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
{
int error;
struct dentry *dentry = NULL, *trap;
- const char *old_name;
+ struct name_snapshot old_name;
trap = lock_rename(new_dir, old_dir);
/* Source or destination directories don't exist? */
@@ -684,19 +684,19 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
goto exit;
- old_name = fsnotify_oldname_init(old_dentry->d_name.name);
+ take_dentry_name_snapshot(&old_name, old_dentry);
error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir),
dentry);
if (error) {
- fsnotify_oldname_free(old_name);
+ release_dentry_name_snapshot(&old_name);
goto exit;
}
d_move(old_dentry, dentry);
- fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name,
+ fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name,
d_is_dir(old_dentry),
NULL, old_dentry);
- fsnotify_oldname_free(old_name);
+ release_dentry_name_snapshot(&old_name);
unlock_rename(new_dir, old_dir);
dput(dentry);
return old_dentry;
diff --git a/fs/exec.c b/fs/exec.c
index 073ae12b396e..0428c34d4773 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -206,8 +206,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
if (write) {
unsigned long size = bprm->vma->vm_end - bprm->vma->vm_start;
- unsigned long ptr_size;
- struct rlimit *rlim;
+ unsigned long ptr_size, limit;
/*
* Since the stack will hold pointers to the strings, we
@@ -236,14 +235,16 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
return page;
/*
- * Limit to 1/4-th the stack size for the argv+env strings.
+ * Limit to 1/4 of the max stack size or 3/4 of _STK_LIM
+ * (whichever is smaller) for the argv+env strings.
* This ensures that:
* - the remaining binfmt code will not run out of stack space,
* - the program will have a reasonable amount of stack left
* to work from.
*/
- rlim = current->signal->rlim;
- if (size > READ_ONCE(rlim[RLIMIT_STACK].rlim_cur) / 4)
+ limit = _STK_LIM / 4 * 3;
+ limit = min(limit, rlimit(RLIMIT_STACK) / 4);
+ if (size > limit)
goto fail;
}
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 8772bfc3415b..45ef9975caec 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -500,6 +500,8 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
lastoff = page_offset(page);
bh = head = page_buffers(page);
do {
+ if (lastoff + bh->b_size <= startoff)
+ goto next;
if (buffer_uptodate(bh) ||
buffer_unwritten(bh)) {
if (whence == SEEK_DATA)
@@ -514,6 +516,7 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
unlock_page(page);
goto out;
}
+next:
lastoff += bh->b_size;
bh = bh->b_this_page;
} while (bh != head);
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index 34038e3598d5..74516efd874c 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -1926,7 +1926,8 @@ retry:
n_desc_blocks = o_desc_blocks +
le16_to_cpu(es->s_reserved_gdt_blocks);
n_group = n_desc_blocks * EXT4_DESC_PER_BLOCK(sb);
- n_blocks_count = n_group * EXT4_BLOCKS_PER_GROUP(sb);
+ n_blocks_count = (ext4_fsblk_t)n_group *
+ EXT4_BLOCKS_PER_GROUP(sb);
n_group--; /* set to last group number */
}
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index 5d09ea585840..c2ee23acf359 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -100,7 +100,7 @@ static ssize_t reserved_clusters_store(struct ext4_attr *a,
int ret;
ret = kstrtoull(skip_spaces(buf), 0, &val);
- if (!ret || val >= clusters)
+ if (ret || val >= clusters)
return -EINVAL;
atomic64_set(&sbi->s_resv_clusters, val);
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index e9a8d676c6bc..83dcf7bfd7b8 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -213,7 +213,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
switch (type) {
case ACL_TYPE_ACCESS:
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
- if (acl) {
+ if (acl && !ipage) {
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
if (error)
return error;
diff --git a/fs/fcntl.c b/fs/fcntl.c
index ee85cd4e136a..62376451bbce 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -740,16 +740,10 @@ static int __init fcntl_init(void)
* Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY
* is defined as O_NONBLOCK on some platforms and not on others.
*/
- BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32(
- O_RDONLY | O_WRONLY | O_RDWR |
- O_CREAT | O_EXCL | O_NOCTTY |
- O_TRUNC | O_APPEND | /* O_NONBLOCK | */
- __O_SYNC | O_DSYNC | FASYNC |
- O_DIRECT | O_LARGEFILE | O_DIRECTORY |
- O_NOFOLLOW | O_NOATIME | O_CLOEXEC |
- __FMODE_EXEC | O_PATH | __O_TMPFILE |
- __FMODE_NONOTIFY
- ));
+ BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ !=
+ HWEIGHT32(
+ (VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) |
+ __FMODE_EXEC | __FMODE_NONOTIFY));
fasync_cache = kmem_cache_create("fasync_cache",
sizeof(struct fasync_struct), 0, SLAB_PANIC, NULL);
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 66b34b14cb6b..f0de8fe294f4 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -55,7 +55,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
{
struct fuse_file *ff;
- ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
+ ff = kzalloc(sizeof(struct fuse_file), GFP_KERNEL);
if (unlikely(!ff))
return NULL;
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 9cd8c92b953d..070901e76653 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -80,9 +80,9 @@ static struct rhashtable_params ht_parms = {
static struct rhashtable gl_hash_table;
-void gfs2_glock_free(struct gfs2_glock *gl)
+static void gfs2_glock_dealloc(struct rcu_head *rcu)
{
- struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
+ struct gfs2_glock *gl = container_of(rcu, struct gfs2_glock, gl_rcu);
if (gl->gl_ops->go_flags & GLOF_ASPACE) {
kmem_cache_free(gfs2_glock_aspace_cachep, gl);
@@ -90,6 +90,13 @@ void gfs2_glock_free(struct gfs2_glock *gl)
kfree(gl->gl_lksb.sb_lvbptr);
kmem_cache_free(gfs2_glock_cachep, gl);
}
+}
+
+void gfs2_glock_free(struct gfs2_glock *gl)
+{
+ struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
+
+ call_rcu(&gl->gl_rcu, gfs2_glock_dealloc);
if (atomic_dec_and_test(&sdp->sd_glock_disposal))
wake_up(&sdp->sd_glock_wait);
}
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index be519416c112..4a9077ec9313 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -367,6 +367,7 @@ struct gfs2_glock {
loff_t end;
} gl_vm;
};
+ struct rcu_head gl_rcu;
struct rhash_head gl_node;
};
diff --git a/fs/mount.h b/fs/mount.h
index 13a4ebbbaa74..37c64bbe840c 100644
--- a/fs/mount.h
+++ b/fs/mount.h
@@ -57,6 +57,7 @@ struct mount {
struct mnt_namespace *mnt_ns; /* containing namespace */
struct mountpoint *mnt_mp; /* where is it mounted */
struct hlist_node mnt_mp_list; /* list mounts with the same mountpoint */
+ struct list_head mnt_umounting; /* list entry for umount propagation */
#ifdef CONFIG_FSNOTIFY
struct hlist_head mnt_fsnotify_marks;
__u32 mnt_fsnotify_mask;
diff --git a/fs/namei.c b/fs/namei.c
index b5dfe2c5b51e..3b2b5c8e0ec7 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4264,11 +4264,11 @@ int vfs_rename2(struct vfsmount *mnt,
{
int error;
bool is_dir = d_is_dir(old_dentry);
- const unsigned char *old_name;
struct inode *source = old_dentry->d_inode;
struct inode *target = new_dentry->d_inode;
bool new_is_dir = false;
unsigned max_links = new_dir->i_sb->s_max_links;
+ struct name_snapshot old_name;
/*
* Check source == target.
@@ -4322,7 +4322,7 @@ int vfs_rename2(struct vfsmount *mnt,
if (error)
return error;
- old_name = fsnotify_oldname_init(old_dentry->d_name.name);
+ take_dentry_name_snapshot(&old_name, old_dentry);
dget(new_dentry);
if (!is_dir || (flags & RENAME_EXCHANGE))
lock_two_nondirectories(source, target);
@@ -4383,14 +4383,14 @@ out:
mutex_unlock(&target->i_mutex);
dput(new_dentry);
if (!error) {
- fsnotify_move(old_dir, new_dir, old_name, is_dir,
+ fsnotify_move(old_dir, new_dir, old_name.name, is_dir,
!(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
if (flags & RENAME_EXCHANGE) {
fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
new_is_dir, NULL, new_dentry);
}
}
- fsnotify_oldname_free(old_name);
+ release_dentry_name_snapshot(&old_name);
return error;
}
diff --git a/fs/namespace.c b/fs/namespace.c
index 0f52d90c356f..f32450c3e72c 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -237,6 +237,7 @@ static struct mount *alloc_vfsmnt(const char *name)
INIT_LIST_HEAD(&mnt->mnt_slave_list);
INIT_LIST_HEAD(&mnt->mnt_slave);
INIT_HLIST_NODE(&mnt->mnt_mp_list);
+ INIT_LIST_HEAD(&mnt->mnt_umounting);
#ifdef CONFIG_FSNOTIFY
INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks);
#endif
diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
index f31fd0dd92c6..b1daeafbea92 100644
--- a/fs/nfs/Kconfig
+++ b/fs/nfs/Kconfig
@@ -121,6 +121,7 @@ config PNFS_FILE_LAYOUT
config PNFS_BLOCK
tristate
depends on NFS_V4_1 && BLK_DEV_DM
+ depends on 64BIT || LBDAF
default NFS_V4
config PNFS_OBJLAYOUT
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 5b21b1ca2341..348e0a05bd18 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1135,11 +1135,13 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
/* Force a full look up iff the parent directory has changed */
if (!nfs_is_exclusive_create(dir, flags) &&
nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) {
-
- if (nfs_lookup_verify_inode(inode, flags)) {
+ error = nfs_lookup_verify_inode(inode, flags);
+ if (error) {
if (flags & LOOKUP_RCU)
return -ECHILD;
- goto out_zap_parent;
+ if (error == -ESTALE)
+ goto out_zap_parent;
+ goto out_error;
}
goto out_valid;
}
@@ -1163,8 +1165,10 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
trace_nfs_lookup_revalidate_exit(dir, dentry, flags, error);
- if (error)
+ if (error == -ESTALE || error == -ENOENT)
goto out_bad;
+ if (error)
+ goto out_error;
if (nfs_compare_fh(NFS_FH(inode), fhandle))
goto out_bad;
if ((error = nfs_refresh_inode(inode, fattr)) != 0)
diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
index e125e55de86d..2603d7589946 100644
--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c
+++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
@@ -30,6 +30,7 @@ void nfs4_ff_layout_free_deviceid(struct nfs4_ff_layout_ds *mirror_ds)
{
nfs4_print_deviceid(&mirror_ds->id_node.deviceid);
nfs4_pnfs_ds_put(mirror_ds->ds);
+ kfree(mirror_ds->ds_versions);
kfree_rcu(mirror_ds, id_node.rcu);
}
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index f714b98cfd74..668ac19af58f 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1241,9 +1241,9 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
return 0;
/* Has the inode gone and changed behind our back? */
if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid)
- return -EIO;
+ return -ESTALE;
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
- return -EIO;
+ return -ESTALE;
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 &&
inode->i_version != fattr->change_attr)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 3f68a25f2169..544672b440de 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -129,7 +129,7 @@ static void next_decode_page(struct nfsd4_compoundargs *argp)
argp->p = page_address(argp->pagelist[0]);
argp->pagelist++;
if (argp->pagelen < PAGE_SIZE) {
- argp->end = argp->p + (argp->pagelen>>2);
+ argp->end = argp->p + XDR_QUADLEN(argp->pagelen);
argp->pagelen = 0;
} else {
argp->end = argp->p + (PAGE_SIZE>>2);
@@ -1246,9 +1246,7 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
argp->pagelen -= pages * PAGE_SIZE;
len -= pages * PAGE_SIZE;
- argp->p = (__be32 *)page_address(argp->pagelist[0]);
- argp->pagelist++;
- argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
+ next_decode_page(argp);
}
argp->p += XDR_QUADLEN(len);
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index db39de2dd4cb..a64adc2fced9 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -104,16 +104,20 @@ int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
if (unlikely(!fsnotify_inode_watches_children(p_inode)))
__fsnotify_update_child_dentry_flags(p_inode);
else if (p_inode->i_fsnotify_mask & mask) {
+ struct name_snapshot name;
+
/* we are notifying a parent so come up with the new mask which
* specifies these are events which came from a child. */
mask |= FS_EVENT_ON_CHILD;
+ take_dentry_name_snapshot(&name, dentry);
if (path)
ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
- dentry->d_name.name, 0);
+ name.name, 0);
else
ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
- dentry->d_name.name, 0);
+ name.name, 0);
+ release_dentry_name_snapshot(&name);
}
dput(parent);
diff --git a/fs/open.c b/fs/open.c
index e70cca15c976..1fd96c5d3895 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -898,6 +898,12 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
int lookup_flags = 0;
int acc_mode;
+ /*
+ * Clear out all open flags we don't know about so that we don't report
+ * them in fcntl(F_GETFD) or similar interfaces.
+ */
+ flags &= VALID_OPEN_FLAGS;
+
if (flags & (O_CREAT | __O_TMPFILE))
op->mode = (mode & S_IALLUGO) | S_IFREG;
else
diff --git a/fs/pnode.c b/fs/pnode.c
index e4e428d621e9..ddb846f878b8 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -24,6 +24,11 @@ static inline struct mount *first_slave(struct mount *p)
return list_entry(p->mnt_slave_list.next, struct mount, mnt_slave);
}
+static inline struct mount *last_slave(struct mount *p)
+{
+ return list_entry(p->mnt_slave_list.prev, struct mount, mnt_slave);
+}
+
static inline struct mount *next_slave(struct mount *p)
{
return list_entry(p->mnt_slave.next, struct mount, mnt_slave);
@@ -164,6 +169,19 @@ static struct mount *propagation_next(struct mount *m,
}
}
+static struct mount *skip_propagation_subtree(struct mount *m,
+ struct mount *origin)
+{
+ /*
+ * Advance m such that propagation_next will not return
+ * the slaves of m.
+ */
+ if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
+ m = last_slave(m);
+
+ return m;
+}
+
static struct mount *next_group(struct mount *m, struct mount *origin)
{
while (1) {
@@ -415,65 +433,104 @@ void propagate_mount_unlock(struct mount *mnt)
}
}
-/*
- * Mark all mounts that the MNT_LOCKED logic will allow to be unmounted.
- */
-static void mark_umount_candidates(struct mount *mnt)
+static void umount_one(struct mount *mnt, struct list_head *to_umount)
{
- struct mount *parent = mnt->mnt_parent;
- struct mount *m;
-
- BUG_ON(parent == mnt);
-
- for (m = propagation_next(parent, parent); m;
- m = propagation_next(m, parent)) {
- struct mount *child = __lookup_mnt(&m->mnt,
- mnt->mnt_mountpoint);
- if (!child || (child->mnt.mnt_flags & MNT_UMOUNT))
- continue;
- if (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m)) {
- SET_MNT_MARK(child);
- }
- }
+ CLEAR_MNT_MARK(mnt);
+ mnt->mnt.mnt_flags |= MNT_UMOUNT;
+ list_del_init(&mnt->mnt_child);
+ list_del_init(&mnt->mnt_umounting);
+ list_move_tail(&mnt->mnt_list, to_umount);
}
/*
* NOTE: unmounting 'mnt' naturally propagates to all other mounts its
* parent propagates to.
*/
-static void __propagate_umount(struct mount *mnt)
+static bool __propagate_umount(struct mount *mnt,
+ struct list_head *to_umount,
+ struct list_head *to_restore)
{
- struct mount *parent = mnt->mnt_parent;
- struct mount *m;
+ bool progress = false;
+ struct mount *child;
- BUG_ON(parent == mnt);
+ /*
+ * The state of the parent won't change if this mount is
+ * already unmounted or marked as without children.
+ */
+ if (mnt->mnt.mnt_flags & (MNT_UMOUNT | MNT_MARKED))
+ goto out;
- for (m = propagation_next(parent, parent); m;
- m = propagation_next(m, parent)) {
- struct mount *topper;
- struct mount *child = __lookup_mnt(&m->mnt,
- mnt->mnt_mountpoint);
- /*
- * umount the child only if the child has no children
- * and the child is marked safe to unmount.
- */
- if (!child || !IS_MNT_MARKED(child))
+ /* Verify topper is the only grandchild that has not been
+ * speculatively unmounted.
+ */
+ list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
+ if (child->mnt_mountpoint == mnt->mnt.mnt_root)
continue;
- CLEAR_MNT_MARK(child);
+ if (!list_empty(&child->mnt_umounting) && IS_MNT_MARKED(child))
+ continue;
+ /* Found a mounted child */
+ goto children;
+ }
- /* If there is exactly one mount covering all of child
- * replace child with that mount.
- */
- topper = find_topper(child);
- if (topper)
- mnt_change_mountpoint(child->mnt_parent, child->mnt_mp,
- topper);
+ /* Mark mounts that can be unmounted if not locked */
+ SET_MNT_MARK(mnt);
+ progress = true;
+
+ /* If a mount is without children and not locked umount it. */
+ if (!IS_MNT_LOCKED(mnt)) {
+ umount_one(mnt, to_umount);
+ } else {
+children:
+ list_move_tail(&mnt->mnt_umounting, to_restore);
+ }
+out:
+ return progress;
+}
+
+static void umount_list(struct list_head *to_umount,
+ struct list_head *to_restore)
+{
+ struct mount *mnt, *child, *tmp;
+ list_for_each_entry(mnt, to_umount, mnt_list) {
+ list_for_each_entry_safe(child, tmp, &mnt->mnt_mounts, mnt_child) {
+ /* topper? */
+ if (child->mnt_mountpoint == mnt->mnt.mnt_root)
+ list_move_tail(&child->mnt_umounting, to_restore);
+ else
+ umount_one(child, to_umount);
+ }
+ }
+}
- if (list_empty(&child->mnt_mounts)) {
- list_del_init(&child->mnt_child);
- child->mnt.mnt_flags |= MNT_UMOUNT;
- list_move_tail(&child->mnt_list, &mnt->mnt_list);
+static void restore_mounts(struct list_head *to_restore)
+{
+ /* Restore mounts to a clean working state */
+ while (!list_empty(to_restore)) {
+ struct mount *mnt, *parent;
+ struct mountpoint *mp;
+
+ mnt = list_first_entry(to_restore, struct mount, mnt_umounting);
+ CLEAR_MNT_MARK(mnt);
+ list_del_init(&mnt->mnt_umounting);
+
+ /* Should this mount be reparented? */
+ mp = mnt->mnt_mp;
+ parent = mnt->mnt_parent;
+ while (parent->mnt.mnt_flags & MNT_UMOUNT) {
+ mp = parent->mnt_mp;
+ parent = parent->mnt_parent;
}
+ if (parent != mnt->mnt_parent)
+ mnt_change_mountpoint(parent, mp, mnt);
+ }
+}
+
+static void cleanup_umount_visitations(struct list_head *visited)
+{
+ while (!list_empty(visited)) {
+ struct mount *mnt =
+ list_first_entry(visited, struct mount, mnt_umounting);
+ list_del_init(&mnt->mnt_umounting);
}
}
@@ -487,12 +544,69 @@ static void __propagate_umount(struct mount *mnt)
int propagate_umount(struct list_head *list)
{
struct mount *mnt;
+ LIST_HEAD(to_restore);
+ LIST_HEAD(to_umount);
+ LIST_HEAD(visited);
+
+ /* Find candidates for unmounting */
+ list_for_each_entry_reverse(mnt, list, mnt_list) {
+ struct mount *parent = mnt->mnt_parent;
+ struct mount *m;
+
+ /*
+ * If this mount has already been visited it is known that it's
+ * entire peer group and all of their slaves in the propagation
+ * tree for the mountpoint has already been visited and there is
+ * no need to visit them again.
+ */
+ if (!list_empty(&mnt->mnt_umounting))
+ continue;
+
+ list_add_tail(&mnt->mnt_umounting, &visited);
+ for (m = propagation_next(parent, parent); m;
+ m = propagation_next(m, parent)) {
+ struct mount *child = __lookup_mnt(&m->mnt,
+ mnt->mnt_mountpoint);
+ if (!child)
+ continue;
+
+ if (!list_empty(&child->mnt_umounting)) {
+ /*
+ * If the child has already been visited it is
+ * know that it's entire peer group and all of
+ * their slaves in the propgation tree for the
+ * mountpoint has already been visited and there
+ * is no need to visit this subtree again.
+ */
+ m = skip_propagation_subtree(m, parent);
+ continue;
+ } else if (child->mnt.mnt_flags & MNT_UMOUNT) {
+ /*
+ * We have come accross an partially unmounted
+ * mount in list that has not been visited yet.
+ * Remember it has been visited and continue
+ * about our merry way.
+ */
+ list_add_tail(&child->mnt_umounting, &visited);
+ continue;
+ }
+
+ /* Check the child and parents while progress is made */
+ while (__propagate_umount(child,
+ &to_umount, &to_restore)) {
+ /* Is the parent a umount candidate? */
+ child = child->mnt_parent;
+ if (list_empty(&child->mnt_umounting))
+ break;
+ }
+ }
+ }
- list_for_each_entry_reverse(mnt, list, mnt_list)
- mark_umount_candidates(mnt);
+ umount_list(&to_umount, &to_restore);
+ restore_mounts(&to_restore);
+ cleanup_umount_visitations(&visited);
+ list_splice_tail(&to_umount, list);
- list_for_each_entry(mnt, list, mnt_list)
- __propagate_umount(mnt);
return 0;
}
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index 8d1e5e2db6a1..c9e4bc47c79d 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -434,7 +434,7 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt,
for (i = 0; i < cxt->max_dump_cnt; i++) {
cxt->przs[i] = persistent_ram_new(*paddr, cxt->record_size, 0,
&cxt->ecc_info,
- cxt->memtype);
+ cxt->memtype, 0);
if (IS_ERR(cxt->przs[i])) {
err = PTR_ERR(cxt->przs[i]);
dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n",
@@ -471,7 +471,8 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt,
return -ENOMEM;
}
- *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info, cxt->memtype);
+ *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info,
+ cxt->memtype, 0);
if (IS_ERR(*prz)) {
int err = PTR_ERR(*prz);
diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c
index 3975deec02f8..e11672aa4575 100644
--- a/fs/pstore/ram_core.c
+++ b/fs/pstore/ram_core.c
@@ -48,16 +48,15 @@ static inline size_t buffer_start(struct persistent_ram_zone *prz)
return atomic_read(&prz->buffer->start);
}
-static DEFINE_RAW_SPINLOCK(buffer_lock);
-
/* increase and wrap the start pointer, returning the old value */
static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
{
int old;
int new;
- unsigned long flags;
+ unsigned long flags = 0;
- raw_spin_lock_irqsave(&buffer_lock, flags);
+ if (!(prz->flags & PRZ_FLAG_NO_LOCK))
+ raw_spin_lock_irqsave(&prz->buffer_lock, flags);
old = atomic_read(&prz->buffer->start);
new = old + a;
@@ -65,7 +64,8 @@ static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
new -= prz->buffer_size;
atomic_set(&prz->buffer->start, new);
- raw_spin_unlock_irqrestore(&buffer_lock, flags);
+ if (!(prz->flags & PRZ_FLAG_NO_LOCK))
+ raw_spin_unlock_irqrestore(&prz->buffer_lock, flags);
return old;
}
@@ -75,9 +75,10 @@ static void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
{
size_t old;
size_t new;
- unsigned long flags;
+ unsigned long flags = 0;
- raw_spin_lock_irqsave(&buffer_lock, flags);
+ if (!(prz->flags & PRZ_FLAG_NO_LOCK))
+ raw_spin_lock_irqsave(&prz->buffer_lock, flags);
old = atomic_read(&prz->buffer->size);
if (old == prz->buffer_size)
@@ -89,7 +90,8 @@ static void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
atomic_set(&prz->buffer->size, new);
exit:
- raw_spin_unlock_irqrestore(&buffer_lock, flags);
+ if (!(prz->flags & PRZ_FLAG_NO_LOCK))
+ raw_spin_unlock_irqrestore(&prz->buffer_lock, flags);
}
static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
@@ -491,6 +493,7 @@ static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
prz->buffer->sig);
}
+ /* Rewind missing or invalid memory area. */
prz->buffer->sig = sig;
persistent_ram_zap(prz);
@@ -517,7 +520,7 @@ void persistent_ram_free(struct persistent_ram_zone *prz)
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info,
- unsigned int memtype)
+ unsigned int memtype, u32 flags)
{
struct persistent_ram_zone *prz;
int ret = -ENOMEM;
@@ -528,6 +531,10 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
goto err;
}
+ /* Initialize general buffer state. */
+ raw_spin_lock_init(&prz->buffer_lock);
+ prz->flags = flags;
+
ret = persistent_ram_buffer_map(start, size, prz, memtype);
if (ret)
goto err;
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index 6076c342dae6..5ac0b0bbb0ec 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -104,12 +104,19 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd,
{
long err = -ENOTTY;
struct file *lower_file;
+ const struct cred *saved_cred = NULL;
+ struct dentry *dentry = file->f_path.dentry;
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
lower_file = sdcardfs_lower_file(file);
/* XXX: use vfs_ioctl if/when VFS exports it */
if (!lower_file || !lower_file->f_op)
goto out;
+
+ /* save current_cred and override it */
+ OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file)));
+
if (lower_file->f_op->unlocked_ioctl)
err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
@@ -117,6 +124,7 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd,
if (!err)
sdcardfs_copy_and_fix_attrs(file_inode(file),
file_inode(lower_file));
+ REVERT_CRED(saved_cred);
out:
return err;
}
@@ -127,15 +135,23 @@ static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd,
{
long err = -ENOTTY;
struct file *lower_file;
+ const struct cred *saved_cred = NULL;
+ struct dentry *dentry = file->f_path.dentry;
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
lower_file = sdcardfs_lower_file(file);
/* XXX: use vfs_ioctl if/when VFS exports it */
if (!lower_file || !lower_file->f_op)
goto out;
+
+ /* save current_cred and override it */
+ OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file)));
+
if (lower_file->f_op->compat_ioctl)
err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
+ REVERT_CRED(saved_cred);
out:
return err;
}
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 60fea424835f..103dc45a131f 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -766,13 +766,9 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
* afterwards in the other cases: we fsstack_copy_inode_size from
* the lower level.
*/
- if (current->mm)
- down_write(&current->mm->mmap_sem);
if (ia->ia_valid & ATTR_SIZE) {
err = inode_newsize_ok(&tmp, ia->ia_size);
if (err) {
- if (current->mm)
- up_write(&current->mm->mmap_sem);
goto out;
}
truncate_setsize(inode, ia->ia_size);
@@ -795,8 +791,6 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */
NULL);
mutex_unlock(&d_inode(lower_dentry)->i_mutex);
- if (current->mm)
- up_write(&current->mm->mmap_sem);
if (err)
goto out;
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index 3c5b51d49d21..80825b287836 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -364,41 +364,34 @@ out:
return err;
}
-/* A feature which supports mount_nodev() with options */
-static struct dentry *mount_nodev_with_options(struct vfsmount *mnt,
- struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data,
- int (*fill_super)(struct vfsmount *, struct super_block *,
- const char *, void *, int))
+struct sdcardfs_mount_private {
+ struct vfsmount *mnt;
+ const char *dev_name;
+ void *raw_data;
+};
+static int __sdcardfs_fill_super(
+ struct super_block *sb,
+ void *_priv, int silent)
{
- int error;
- struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
-
- if (IS_ERR(s))
- return ERR_CAST(s);
-
- s->s_flags = flags;
+ struct sdcardfs_mount_private *priv = _priv;
- error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0);
- if (error) {
- deactivate_locked_super(s);
- return ERR_PTR(error);
- }
- s->s_flags |= MS_ACTIVE;
- return dget(s->s_root);
+ return sdcardfs_read_super(priv->mnt,
+ sb, priv->dev_name, priv->raw_data, silent);
}
static struct dentry *sdcardfs_mount(struct vfsmount *mnt,
struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
- /*
- * dev_name is a lower_path_name,
- * raw_data is a option string.
- */
- return mount_nodev_with_options(mnt, fs_type, flags, dev_name,
- raw_data, sdcardfs_read_super);
+ struct sdcardfs_mount_private priv = {
+ .mnt = mnt,
+ .dev_name = dev_name,
+ .raw_data = raw_data
+ };
+
+ return mount_nodev(fs_type, flags,
+ &priv, __sdcardfs_fill_super);
}
static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type,
@@ -423,7 +416,7 @@ void sdcardfs_kill_sb(struct super_block *sb)
list_del(&sbi->list);
mutex_unlock(&sdcardfs_super_list_lock);
}
- generic_shutdown_super(sb);
+ kill_anon_super(sb);
}
static struct file_system_type sdcardfs_fs_type = {
diff --git a/fs/seq_file.c b/fs/seq_file.c
index d672e2fec459..6dc4296eed62 100644
--- a/fs/seq_file.c
+++ b/fs/seq_file.c
@@ -72,9 +72,10 @@ int seq_open(struct file *file, const struct seq_operations *op)
mutex_init(&p->lock);
p->op = op;
-#ifdef CONFIG_USER_NS
- p->user_ns = file->f_cred->user_ns;
-#endif
+
+ // No refcounting: the lifetime of 'p' is constrained
+ // to the lifetime of the file.
+ p->file = file;
/*
* Wrappers around seq_open(e.g. swaps_open) need to be
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 7be3166ba553..0e659d9c69a1 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -1235,8 +1235,8 @@ int udf_setsize(struct inode *inode, loff_t newsize)
return err;
}
set_size:
- truncate_setsize(inode, newsize);
up_write(&iinfo->i_data_sem);
+ truncate_setsize(inode, newsize);
} else {
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
down_write(&iinfo->i_data_sem);
@@ -1253,9 +1253,9 @@ set_size:
udf_get_block);
if (err)
return err;
+ truncate_setsize(inode, newsize);
down_write(&iinfo->i_data_sem);
udf_clear_extent_cache(inode);
- truncate_setsize(inode, newsize);
udf_truncate_extents(inode);
up_write(&iinfo->i_data_sem);
}
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 187b80267ff9..a9063ac50c4e 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -1426,6 +1426,26 @@ __xfs_get_blocks(
if (error)
goto out_unlock;
+ /*
+ * The only time we can ever safely find delalloc blocks on direct I/O
+ * is a dio write to post-eof speculative preallocation. All other
+ * scenarios are indicative of a problem or misuse (such as mixing
+ * direct and mapped I/O).
+ *
+ * The file may be unmapped by the time we get here so we cannot
+ * reliably fail the I/O based on mapping. Instead, fail the I/O if this
+ * is a read or a write within eof. Otherwise, carry on but warn as a
+ * precuation if the file happens to be mapped.
+ */
+ if (direct && imap.br_startblock == DELAYSTARTBLOCK) {
+ if (!create || offset < i_size_read(VFS_I(ip))) {
+ WARN_ON_ONCE(1);
+ error = -EIO;
+ goto out_unlock;
+ }
+ WARN_ON_ONCE(mapping_mapped(VFS_I(ip)->i_mapping));
+ }
+
/* for DAX, we convert unwritten extents directly */
if (create &&
(!nimaps ||
@@ -1525,7 +1545,6 @@ __xfs_get_blocks(
set_buffer_new(bh_result);
if (imap.br_startblock == DELAYSTARTBLOCK) {
- BUG_ON(direct);
if (create) {
set_buffer_uptodate(bh_result);
set_buffer_mapped(bh_result);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 6555fdbee88a..f710a7075c0e 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -652,12 +652,15 @@ struct drm_encoder {
* @pt_scan_info: PT scan info obtained from the VCDB of EDID
* @it_scan_info: IT scan info obtained from the VCDB of EDID
* @ce_scan_info: CE scan info obtained from the VCDB of EDID
+ * @color_enc_fmt: Colorimetry encoding formats of sink
* @hdr_eotf: Electro optical transfer function obtained from HDR block
* @hdr_metadata_type_one: Metadata type one obtained from HDR block
* @hdr_max_luminance: desired max luminance obtained from HDR block
* @hdr_avg_luminance: desired avg luminance obtained from HDR block
* @hdr_min_luminance: desired min luminance obtained from HDR block
* @hdr_supported: does the sink support HDR content
+ * @rgb_qs: does the sink declare RGB selectable quantization range
+ * @yuv_qs: does the sink declare YCC selectable quantization range
* @edid_corrupt: indicates whether the last read EDID was corrupt
* @debugfs_entry: debugfs directory for this connector
* @state: current atomic state for this connector
@@ -740,12 +743,15 @@ struct drm_connector {
u8 pt_scan_info;
u8 it_scan_info;
u8 ce_scan_info;
+ u8 color_enc_fmt;
u32 hdr_eotf;
bool hdr_metadata_type_one;
u32 hdr_max_luminance;
u32 hdr_avg_luminance;
u32 hdr_min_luminance;
bool hdr_supported;
+ bool rgb_qs;
+ bool yuv_qs;
/* Flag for raw EDID header corruption - used in Displayport
* compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6
*/
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 69cb2ba37116..c0884e120041 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -214,6 +214,15 @@ struct detailed_timing {
#define DRM_EDID_YCBCR420_DC_36 (1 << 1)
#define DRM_EDID_YCBCR420_DC_30 (1 << 0)
+#define DRM_EDID_COLORIMETRY_xvYCC_601 (1 << 0)
+#define DRM_EDID_COLORIMETRY_xvYCC_709 (1 << 1)
+#define DRM_EDID_COLORIMETRY_sYCC_601 (1 << 2)
+#define DRM_EDID_COLORIMETRY_ADBYCC_601 (1 << 3)
+#define DRM_EDID_COLORIMETRY_ADB_RGB (1 << 4)
+#define DRM_EDID_COLORIMETRY_BT2020_CYCC (1 << 5)
+#define DRM_EDID_COLORIMETRY_BT2020_YCC (1 << 6)
+#define DRM_EDID_COLORIMETRY_BT2020_RGB (1 << 7)
+
/* ELD Header Block */
#define DRM_ELD_HEADER_BLOCK_SIZE 4
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index 85a868ccb493..8397dc235e84 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -16,6 +16,7 @@
#ifdef CONFIG_CPUSETS
+extern struct static_key cpusets_pre_enable_key;
extern struct static_key cpusets_enabled_key;
static inline bool cpusets_enabled(void)
{
@@ -30,12 +31,14 @@ static inline int nr_cpusets(void)
static inline void cpuset_inc(void)
{
+ static_key_slow_inc(&cpusets_pre_enable_key);
static_key_slow_inc(&cpusets_enabled_key);
}
static inline void cpuset_dec(void)
{
static_key_slow_dec(&cpusets_enabled_key);
+ static_key_slow_dec(&cpusets_pre_enable_key);
}
extern int cpuset_init(void);
@@ -104,7 +107,7 @@ extern void cpuset_print_current_mems_allowed(void);
*/
static inline unsigned int read_mems_allowed_begin(void)
{
- if (!cpusets_enabled())
+ if (!static_key_false(&cpusets_pre_enable_key))
return 0;
return read_seqcount_begin(&current->mems_allowed_seq);
@@ -118,7 +121,7 @@ static inline unsigned int read_mems_allowed_begin(void)
*/
static inline bool read_mems_allowed_retry(unsigned int seq)
{
- if (!cpusets_enabled())
+ if (!static_key_false(&cpusets_enabled_key))
return false;
return read_seqcount_retry(&current->mems_allowed_seq, seq);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 2043af2141c1..01448e01b40d 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -616,5 +616,11 @@ static inline struct inode *d_real_inode(struct dentry *dentry)
return d_backing_inode(d_real(dentry));
}
+struct name_snapshot {
+ const char *name;
+ char inline_name[DNAME_INLINE_LEN];
+};
+void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
+void release_dentry_name_snapshot(struct name_snapshot *);
#endif /* __LINUX_DCACHE_H */
diff --git a/include/linux/device.h b/include/linux/device.h
index 4b4e2d5ce6e7..30c52d70c86d 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -368,6 +368,7 @@ int subsys_virtual_register(struct bus_type *subsys,
* @suspend: Used to put the device to sleep mode, usually to a low power
* state.
* @resume: Used to bring the device from the sleep mode.
+ * @shutdown: Called at shut-down time to quiesce the device.
* @ns_type: Callbacks so sysfs can detemine namespaces.
* @namespace: Namespace of the device belongs to this class.
* @pm: The default device power management operations of this class.
@@ -396,6 +397,7 @@ struct class {
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
+ int (*shutdown)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
const void *(*namespace)(struct device *dev);
diff --git a/include/linux/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/fcntl.h b/include/linux/fcntl.h
index 76ce329e656d..1b48d9c9a561 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -3,6 +3,12 @@
#include <uapi/linux/fcntl.h>
+/* list of all valid flags for the open/openat flags argument: */
+#define VALID_OPEN_FLAGS \
+ (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \
+ O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | __O_SYNC | O_DSYNC | \
+ FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \
+ O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE)
#ifndef force_o_largefile
#define force_o_largefile() (BITS_PER_LONG != 32)
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index 7ee1774edee5..a7789559078b 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -310,35 +310,4 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
}
}
-#if defined(CONFIG_FSNOTIFY) /* notify helpers */
-
-/*
- * fsnotify_oldname_init - save off the old filename before we change it
- */
-static inline const unsigned char *fsnotify_oldname_init(const unsigned char *name)
-{
- return kstrdup(name, GFP_KERNEL);
-}
-
-/*
- * fsnotify_oldname_free - free the name we got from fsnotify_oldname_init
- */
-static inline void fsnotify_oldname_free(const unsigned char *old_name)
-{
- kfree(old_name);
-}
-
-#else /* CONFIG_FSNOTIFY */
-
-static inline const char *fsnotify_oldname_init(const unsigned char *name)
-{
- return NULL;
-}
-
-static inline void fsnotify_oldname_free(const unsigned char *old_name)
-{
-}
-
-#endif /* CONFIG_FSNOTIFY */
-
#endif /* _LINUX_FS_NOTIFY_H */
diff --git a/include/linux/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/init_task.h b/include/linux/init_task.h
index 1c1ff7e4faa4..021b1e9ff6cd 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -15,6 +15,8 @@
#include <net/net_namespace.h>
#include <linux/sched/rt.h>
+#include <asm/thread_info.h>
+
#ifdef CONFIG_SMP
# define INIT_PUSHABLE_TASKS(tsk) \
.pushable_tasks = PLIST_NODE_INIT(tsk.pushable_tasks, MAX_PRIO),
@@ -183,14 +185,21 @@ extern struct task_group root_task_group;
# define INIT_KASAN(tsk)
#endif
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+# define INIT_TASK_TI(tsk) .thread_info = INIT_THREAD_INFO(tsk),
+#else
+# define INIT_TASK_TI(tsk)
+#endif
+
/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x1fffff (=2MB)
*/
#define INIT_TASK(tsk) \
{ \
+ INIT_TASK_TI(tsk) \
.state = 0, \
- .stack = &init_thread_info, \
+ .stack = init_stack, \
.usage = ATOMIC_INIT(2), \
.flags = PF_KTHREAD, \
.prio = MAX_PRIO-20, \
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index c11a5c4afece..a4b817c5e4fc 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -40,6 +40,14 @@ enum ipa_nat_en_type {
};
/**
+ * enum ipa_ipv6ct_en_type - IPv6CT setting type in IPA end-point
+ */
+enum ipa_ipv6ct_en_type {
+ IPA_BYPASS_IPV6CT,
+ IPA_ENABLE_IPV6CT,
+};
+
+/**
* enum ipa_mode_type - mode setting type in IPA end-point
* @BASIC: basic mode
* @ENABLE_FRAMING_HDLC: not currently supported
@@ -119,6 +127,19 @@ struct ipa_ep_cfg_nat {
};
/**
+ * struct ipa_ep_cfg_conn_track - IPv6 Connection tracking configuration in
+ * IPA end-point
+ * @conn_track_en: Defines speculative conn_track action, means if specific
+ * pipe needs to have UL/DL IPv6 Connection Tracking or Bypass
+ * IPv6 Connection Tracking. 0: Bypass IPv6 Connection Tracking
+ * 1: IPv6 UL/DL Connection Tracking.
+ * Valid for Input Pipes only (IPA consumer)
+ */
+struct ipa_ep_cfg_conn_track {
+ enum ipa_ipv6ct_en_type conn_track_en;
+};
+
+/**
* struct ipa_ep_cfg_hdr - header configuration in IPA end-point
*
* @hdr_len:Header length in bytes to be added/removed. Assuming
@@ -386,7 +407,8 @@ struct ipa_ep_cfg_seq {
/**
* struct ipa_ep_cfg - configuration of IPA end-point
- * @nat: NAT parmeters
+ * @nat: NAT parameters
+ * @conn_track: IPv6CT parameters
* @hdr: Header parameters
* @hdr_ext: Extended header parameters
* @mode: Mode parameters
@@ -400,6 +422,7 @@ struct ipa_ep_cfg_seq {
*/
struct ipa_ep_cfg {
struct ipa_ep_cfg_nat nat;
+ struct ipa_ep_cfg_conn_track conn_track;
struct ipa_ep_cfg_hdr hdr;
struct ipa_ep_cfg_hdr_ext hdr_ext;
struct ipa_ep_cfg_mode mode;
diff --git a/include/linux/kdb.h b/include/linux/kdb.h
index a19bcf9e762e..410decacff8f 100644
--- a/include/linux/kdb.h
+++ b/include/linux/kdb.h
@@ -177,7 +177,7 @@ extern int kdb_get_kbd_char(void);
static inline
int kdb_process_cpu(const struct task_struct *p)
{
- unsigned int cpu = task_thread_info(p)->cpu;
+ unsigned int cpu = task_cpu(p);
if (cpu > num_possible_cpus())
cpu = 0;
return cpu;
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index e4937bbeae2c..33316a1ae98f 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -515,6 +515,10 @@ struct mm_struct {
*/
bool tlb_flush_pending;
#endif
+#ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
+ /* See flush_tlb_batched_pending() */
+ bool tlb_flush_batched;
+#endif
struct uprobes_state uprobes_state;
#ifdef CONFIG_X86_INTEL_MPX
/* address of the bounds directory */
diff --git a/include/linux/msm_audio_ion.h b/include/linux/msm_audio_ion.h
index 0761b880ca88..9e77ac317c28 100644
--- a/include/linux/msm_audio_ion.h
+++ b/include/linux/msm_audio_ion.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,12 @@
#include <sound/pcm.h>
#include <linux/msm_ion.h>
+enum {
+ HLOS_TO_ADSP = 1,
+ ADSP_TO_HLOS,
+};
+
+#define VMID_CP_ADSP_SHARED 33
int msm_audio_ion_alloc(const char *name, struct ion_client **client,
struct ion_handle **handle, size_t bufsz,
@@ -26,6 +32,7 @@ int msm_audio_ion_import(const char *name, struct ion_client **client,
struct ion_handle **handle, int fd,
unsigned long *ionflag, size_t bufsz,
ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr);
+
int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle);
int msm_audio_ion_mmap(struct audio_buffer *substream,
struct vm_area_struct *vma);
@@ -42,4 +49,13 @@ int msm_audio_ion_import_legacy(const char *name, struct ion_client *client,
int msm_audio_ion_free_legacy(struct ion_client *client,
struct ion_handle *handle);
u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa);
+
+int msm_audio_ion_phys_assign(const char *name, struct ion_client **client,
+ struct ion_handle **handle,
+ int fd, ion_phys_addr_t *paddr,
+ size_t *pa_len, u8 assign_type);
+int msm_audio_ion_phys_free(struct ion_client *client,
+ struct ion_handle *handle,
+ ion_phys_addr_t *paddr,
+ size_t *pa_len, u8 assign_type);
#endif /* _LINUX_MSM_AUDIO_ION_H */
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 05fde31b6dc6..b64825d6ad26 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -785,6 +785,10 @@ int genphy_read_status(struct phy_device *phydev);
int genphy_suspend(struct phy_device *phydev);
int genphy_resume(struct phy_device *phydev);
int genphy_soft_reset(struct phy_device *phydev);
+static inline int genphy_no_soft_reset(struct phy_device *phydev)
+{
+ return 0;
+}
void phy_driver_unregister(struct phy_driver *drv);
void phy_drivers_unregister(struct phy_driver *drv, int n);
int phy_driver_register(struct phy_driver *new_driver);
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index b584e353306d..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,
@@ -249,6 +251,12 @@ enum power_supply_property {
POWER_SUPPLY_PROP_HW_CURRENT_MAX,
POWER_SUPPLY_PROP_REAL_TYPE,
POWER_SUPPLY_PROP_PR_SWAP,
+ POWER_SUPPLY_PROP_CC_STEP,
+ POWER_SUPPLY_PROP_CC_STEP_SEL,
+ POWER_SUPPLY_PROP_SW_JEITA_ENABLED,
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_PD_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h
index 45ac5a0d29ee..7097a45dbc25 100644
--- a/include/linux/pstore_ram.h
+++ b/include/linux/pstore_ram.h
@@ -24,6 +24,13 @@
#include <linux/list.h>
#include <linux/types.h>
+/*
+ * Choose whether access to the RAM zone requires locking or not. If a zone
+ * can be written to from different CPUs like with ftrace for example, then
+ * PRZ_FLAG_NO_LOCK is used. For all other cases, locking is required.
+ */
+#define PRZ_FLAG_NO_LOCK BIT(0)
+
struct persistent_ram_buffer;
struct rs_control;
@@ -40,6 +47,8 @@ struct persistent_ram_zone {
void *vaddr;
struct persistent_ram_buffer *buffer;
size_t buffer_size;
+ u32 flags;
+ raw_spinlock_t buffer_lock;
/* ECC correction */
char *par_buffer;
@@ -55,7 +64,7 @@ struct persistent_ram_zone {
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info,
- unsigned int memtype);
+ unsigned int memtype, u32 flags);
void persistent_ram_free(struct persistent_ram_zone *prz);
void persistent_ram_zap(struct persistent_ram_zone *prz);
diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h
index b025df568259..bbc4625a2d39 100644
--- a/include/linux/qpnp/qpnp-revid.h
+++ b/include/linux/qpnp/qpnp-revid.h
@@ -252,6 +252,7 @@ struct pmic_revid_data {
u8 pmic_subtype;
const char *pmic_name;
int fab_id;
+ int tp_rev;
};
#ifdef CONFIG_QPNP_REVID
diff --git a/include/linux/restart_block.h b/include/linux/restart_block.h
new file mode 100644
index 000000000000..0d905d8ec553
--- /dev/null
+++ b/include/linux/restart_block.h
@@ -0,0 +1,51 @@
+/*
+ * Common syscall restarting data
+ */
+#ifndef __LINUX_RESTART_BLOCK_H
+#define __LINUX_RESTART_BLOCK_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+struct timespec;
+struct compat_timespec;
+struct pollfd;
+
+/*
+ * System call restart block.
+ */
+struct restart_block {
+ long (*fn)(struct restart_block *);
+ union {
+ /* For futex_wait and futex_wait_requeue_pi */
+ struct {
+ u32 __user *uaddr;
+ u32 val;
+ u32 flags;
+ u32 bitset;
+ u64 time;
+ u32 __user *uaddr2;
+ } futex;
+ /* For nanosleep */
+ struct {
+ clockid_t clockid;
+ struct timespec __user *rmtp;
+#ifdef CONFIG_COMPAT
+ struct compat_timespec __user *compat_rmtp;
+#endif
+ u64 expires;
+ } nanosleep;
+ /* For poll */
+ struct {
+ struct pollfd __user *ufds;
+ int nfds;
+ int has_timeout;
+ unsigned long tv_sec;
+ unsigned long tv_nsec;
+ } poll;
+ };
+};
+
+extern long do_no_restart_syscall(struct restart_block *parm);
+
+#endif /* __LINUX_RESTART_BLOCK_H */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 57042d91ae9c..2716faadc618 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -882,6 +882,16 @@ struct signal_struct {
#define SIGNAL_UNKILLABLE 0x00000040 /* for init: ignore fatal signals */
+#define SIGNAL_STOP_MASK (SIGNAL_CLD_MASK | SIGNAL_STOP_STOPPED | \
+ SIGNAL_STOP_CONTINUED)
+
+static inline void signal_set_stop_flags(struct signal_struct *sig,
+ unsigned int flags)
+{
+ WARN_ON(sig->flags & (SIGNAL_GROUP_EXIT|SIGNAL_GROUP_COREDUMP));
+ sig->flags = (sig->flags & ~SIGNAL_STOP_MASK) | flags;
+}
+
/* If true, all threads except ->group_exit_task have pending SIGKILL */
static inline int signal_group_exit(const struct signal_struct *sig)
{
@@ -1601,6 +1611,13 @@ struct tlbflush_unmap_batch {
};
struct task_struct {
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ /*
+ * For reasons of header soup (see current_thread_info()), this
+ * must be the first element of task_struct.
+ */
+ struct thread_info thread_info;
+#endif
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
void *stack;
atomic_t usage;
@@ -1610,6 +1627,9 @@ struct task_struct {
#ifdef CONFIG_SMP
struct llist_node wake_entry;
int on_cpu;
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ unsigned int cpu; /* current CPU */
+#endif
unsigned int wakee_flips;
unsigned long wakee_flip_decay_ts;
struct task_struct *last_wakee;
@@ -2181,22 +2201,6 @@ static inline pid_t task_tgid_nr(struct task_struct *tsk)
static inline int pid_alive(const struct task_struct *p);
static inline pid_t task_tgid_nr_ns(struct task_struct *tsk, struct pid_namespace *ns);
-static inline pid_t task_ppid_nr_ns(const struct task_struct *tsk, struct pid_namespace *ns)
-{
- pid_t pid = 0;
-
- rcu_read_lock();
- if (pid_alive(tsk))
- pid = task_tgid_nr_ns(rcu_dereference(tsk->real_parent), ns);
- rcu_read_unlock();
-
- return pid;
-}
-
-static inline pid_t task_ppid_nr(const struct task_struct *tsk)
-{
- return task_ppid_nr_ns(tsk, &init_pid_ns);
-}
static inline pid_t task_pgrp_nr_ns(struct task_struct *tsk,
struct pid_namespace *ns)
@@ -2231,6 +2235,23 @@ static inline pid_t task_tgid_vnr(struct task_struct *tsk)
return __task_pid_nr_ns(tsk, __PIDTYPE_TGID, NULL);
}
+static inline pid_t task_ppid_nr_ns(const struct task_struct *tsk, struct pid_namespace *ns)
+{
+ pid_t pid = 0;
+
+ rcu_read_lock();
+ if (pid_alive(tsk))
+ pid = task_tgid_nr_ns(rcu_dereference(tsk->real_parent), ns);
+ rcu_read_unlock();
+
+ return pid;
+}
+
+static inline pid_t task_ppid_nr(const struct task_struct *tsk)
+{
+ return task_ppid_nr_ns(tsk, &init_pid_ns);
+}
+
/* obsolete, do not use */
static inline pid_t task_pgrp_nr(struct task_struct *tsk)
{
@@ -2481,6 +2502,7 @@ extern void do_set_cpus_allowed(struct task_struct *p,
extern int set_cpus_allowed_ptr(struct task_struct *p,
const struct cpumask *new_mask);
+extern bool cpupri_check_rt(void);
#else
static inline void do_set_cpus_allowed(struct task_struct *p,
const struct cpumask *new_mask)
@@ -2493,6 +2515,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 {
@@ -2753,7 +2779,9 @@ extern void set_curr_task(int cpu, struct task_struct *p);
void yield(void);
union thread_union {
+#ifndef CONFIG_THREAD_INFO_IN_TASK
struct thread_info thread_info;
+#endif
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
@@ -3149,10 +3177,34 @@ static inline void threadgroup_change_end(struct task_struct *tsk)
cgroup_threadgroup_change_end(tsk);
}
-#ifndef __HAVE_THREAD_FUNCTIONS
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+
+static inline struct thread_info *task_thread_info(struct task_struct *task)
+{
+ return &task->thread_info;
+}
+
+/*
+ * When accessing the stack of a non-current task that might exit, use
+ * try_get_task_stack() instead. task_stack_page will return a pointer
+ * that could get freed out from under you.
+ */
+static inline void *task_stack_page(const struct task_struct *task)
+{
+ return task->stack;
+}
+
+#define setup_thread_stack(new,old) do { } while(0)
+
+static inline unsigned long *end_of_stack(const struct task_struct *task)
+{
+ return task->stack;
+}
+
+#elif !defined(__HAVE_THREAD_FUNCTIONS)
#define task_thread_info(task) ((struct thread_info *)(task)->stack)
-#define task_stack_page(task) ((task)->stack)
+#define task_stack_page(task) ((void *)(task)->stack)
static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)
{
@@ -3179,6 +3231,14 @@ static inline unsigned long *end_of_stack(struct task_struct *p)
}
#endif
+
+static inline void *try_get_task_stack(struct task_struct *tsk)
+{
+ return task_stack_page(tsk);
+}
+
+static inline void put_task_stack(struct task_struct *tsk) {}
+
#define task_stack_end_corrupted(task) \
(*(end_of_stack(task)) != STACK_END_MAGIC)
@@ -3189,7 +3249,7 @@ static inline int object_is_on_stack(void *obj)
return (obj >= stack) && (obj < (stack + THREAD_SIZE));
}
-extern void thread_info_cache_init(void);
+extern void thread_stack_cache_init(void);
#ifdef CONFIG_DEBUG_STACK_USAGE
static inline unsigned long stack_not_used(struct task_struct *p)
@@ -3453,7 +3513,11 @@ static inline void ptrace_signal_wake_up(struct task_struct *t, bool resume)
static inline unsigned int task_cpu(const struct task_struct *p)
{
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ return p->cpu;
+#else
return task_thread_info(p)->cpu;
+#endif
}
static inline int task_node(const struct task_struct *p)
diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h
index dde00defbaa5..f3d45dd42695 100644
--- a/include/linux/seq_file.h
+++ b/include/linux/seq_file.h
@@ -7,13 +7,10 @@
#include <linux/mutex.h>
#include <linux/cpumask.h>
#include <linux/nodemask.h>
+#include <linux/fs.h>
+#include <linux/cred.h>
struct seq_operations;
-struct file;
-struct path;
-struct inode;
-struct dentry;
-struct user_namespace;
struct seq_file {
char *buf;
@@ -27,9 +24,7 @@ struct seq_file {
struct mutex lock;
const struct seq_operations *op;
int poll_event;
-#ifdef CONFIG_USER_NS
- struct user_namespace *user_ns;
-#endif
+ const struct file *file;
void *private;
};
@@ -147,7 +142,7 @@ int seq_release_private(struct inode *, struct file *);
static inline struct user_namespace *seq_user_ns(struct seq_file *seq)
{
#ifdef CONFIG_USER_NS
- return seq->user_ns;
+ return seq->file->f_cred->user_ns;
#else
extern struct user_namespace init_user_ns;
return &init_user_ns;
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index b2c1ea2a4739..f276869a945e 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -402,7 +402,7 @@ int uart_resume_port(struct uart_driver *reg, struct uart_port *port);
static inline int uart_tx_stopped(struct uart_port *port)
{
struct tty_struct *tty = port->state->port.tty;
- if (tty->stopped || port->hw_stopped)
+ if ((tty && tty->stopped) || port->hw_stopped)
return 1;
return 0;
}
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 4ef384b172e0..b4e739f04ee6 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -215,7 +215,7 @@ static inline const char *__check_heap_object(const void *ptr,
* (PAGE_SIZE*2). Larger requests are passed to the page allocator.
*/
#define KMALLOC_SHIFT_HIGH (PAGE_SHIFT + 1)
-#define KMALLOC_SHIFT_MAX (MAX_ORDER + PAGE_SHIFT)
+#define KMALLOC_SHIFT_MAX (MAX_ORDER + PAGE_SHIFT - 1)
#ifndef KMALLOC_SHIFT_LOW
#define KMALLOC_SHIFT_LOW 3
#endif
@@ -228,7 +228,7 @@ static inline const char *__check_heap_object(const void *ptr,
* be allocated from the same page.
*/
#define KMALLOC_SHIFT_HIGH PAGE_SHIFT
-#define KMALLOC_SHIFT_MAX 30
+#define KMALLOC_SHIFT_MAX (MAX_ORDER + PAGE_SHIFT - 1)
#ifndef KMALLOC_SHIFT_LOW
#define KMALLOC_SHIFT_LOW 3
#endif
diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h
index 4cf89517783a..8933ecc2bc9f 100644
--- a/include/linux/thread_info.h
+++ b/include/linux/thread_info.h
@@ -9,46 +9,17 @@
#include <linux/types.h>
#include <linux/bug.h>
+#include <linux/restart_block.h>
-struct timespec;
-struct compat_timespec;
-
+#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
- * System call restart block.
+ * For CONFIG_THREAD_INFO_IN_TASK kernels we need <asm/current.h> for the
+ * definition of current, but for !CONFIG_THREAD_INFO_IN_TASK kernels,
+ * including <asm/current.h> can cause a circular dependency on some platforms.
*/
-struct restart_block {
- long (*fn)(struct restart_block *);
- union {
- /* For futex_wait and futex_wait_requeue_pi */
- struct {
- u32 __user *uaddr;
- u32 val;
- u32 flags;
- u32 bitset;
- u64 time;
- u32 __user *uaddr2;
- } futex;
- /* For nanosleep */
- struct {
- clockid_t clockid;
- struct timespec __user *rmtp;
-#ifdef CONFIG_COMPAT
- struct compat_timespec __user *compat_rmtp;
+#include <asm/current.h>
+#define current_thread_info() ((struct thread_info *)current)
#endif
- u64 expires;
- } nanosleep;
- /* For poll */
- struct {
- struct pollfd __user *ufds;
- int nfds;
- int has_timeout;
- unsigned long tv_sec;
- unsigned long tv_nsec;
- } poll;
- };
-};
-
-extern long do_no_restart_syscall(struct restart_block *parm);
#include <linux/bitops.h>
#include <asm/thread_info.h>
diff --git a/include/linux/tick.h b/include/linux/tick.h
index 1732697ea419..d1162e9b7a36 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -105,6 +105,7 @@ extern void tick_nohz_idle_enter(void);
extern void tick_nohz_idle_exit(void);
extern void tick_nohz_irq_exit(void);
extern ktime_t tick_nohz_get_sleep_length(void);
+extern unsigned long tick_nohz_get_idle_calls(void);
extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time);
extern u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time);
#else /* !CONFIG_NO_HZ_COMMON */
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index e888eb9a2eb9..dff7adbc60bb 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -579,9 +579,9 @@ extern void usb_ep0_reinit(struct usb_device *);
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
#define EndpointRequest \
- ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
#define EndpointOutRequest \
- ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
/* class requests from the USB 2.0 hub spec, table 11-15 */
/* GetBusState and SetHubDescriptor are optional, omitted */
diff --git a/include/linux/usb_bam.h b/include/linux/usb_bam.h
index e65fb12c1410..b5b9edaab783 100644
--- a/include/linux/usb_bam.h
+++ b/include/linux/usb_bam.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -191,7 +191,7 @@ struct usb_bam_pipe_connect {
};
/**
- * struct msm_usb_bam_platform_data: pipe connection information
+ * struct msm_usb_bam_data: pipe connection information
* between USB/HSIC BAM and another BAM. USB/HSIC BAM can be
* either src BAM or dst BAM
* @usb_bam_num_pipes: max number of pipes to use.
@@ -211,7 +211,7 @@ struct usb_bam_pipe_connect {
* can work at in bam2bam mode when connected to SS host.
* @enable_hsusb_bam_on_boot: Enable HSUSB BAM (non-NDP) on bootup itself
*/
-struct msm_usb_bam_platform_data {
+struct msm_usb_bam_data {
u8 max_connections;
int usb_bam_num_pipes;
phys_addr_t usb_bam_fifo_baseaddr;
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index ddb440975382..34851bf2e2c8 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -85,6 +85,8 @@ extern void vfio_unregister_iommu_driver(
*/
extern struct vfio_group *vfio_group_get_external_user(struct file *filep);
extern void vfio_group_put_external_user(struct vfio_group *group);
+extern bool vfio_external_group_match_file(struct vfio_group *group,
+ struct file *filep);
extern int vfio_external_user_iommu_id(struct vfio_group *group);
extern long vfio_external_check_extension(struct vfio_group *group,
unsigned long arg);
diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h
index c93364b861d9..bc838936d2c9 100644
--- a/include/linux/wcnss_wlan.h
+++ b/include/linux/wcnss_wlan.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -74,6 +74,8 @@ enum {
#define HAVE_WCNSS_CAL_DOWNLOAD 1
#define HAVE_CBC_DONE 1
#define HAVE_WCNSS_RX_BUFF_COUNT 1
+#define HAVE_WCNSS_SNOC_HIGH_FREQ_VOTING 1
+#define HAVE_WCNSS_5G_DISABLE 1
#define WLAN_MAC_ADDR_SIZE (6)
#define WLAN_RF_REG_ADDR_START_OFFSET 0x3
#define WLAN_RF_REG_DATA_START_OFFSET 0xf
@@ -132,12 +134,16 @@ void wcnss_riva_dump_pmic_regs(void);
int wcnss_xo_auto_detect_enabled(void);
u32 wcnss_get_wlan_rx_buff_count(void);
int wcnss_wlan_iris_xo_mode(void);
+int wcnss_wlan_dual_band_disabled(void);
void wcnss_flush_work(struct work_struct *work);
void wcnss_flush_delayed_work(struct delayed_work *dwork);
void wcnss_init_work(struct work_struct *work , void *callbackptr);
void wcnss_init_delayed_work(struct delayed_work *dwork , void *callbackptr);
int wcnss_get_iris_name(char *iris_version);
void wcnss_dump_stack(struct task_struct *task);
+void wcnss_snoc_vote(bool clk_chk_en);
+int wcnss_parse_voltage_regulator(struct wcnss_wlan_config *wlan_config,
+ struct device *dev);
#ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE
void wcnss_log_debug_regs_on_bite(void);
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 0e32bc71245e..2e04fa5a5b58 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -311,6 +311,7 @@ enum {
__WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */
__WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */
+ __WQ_ORDERED_EXPLICIT = 1 << 18, /* internal: alloc_ordered_workqueue() */
WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */
WQ_MAX_UNBOUND_PER_CPU = 4, /* 4 * #cpus for unbound wq */
@@ -408,7 +409,8 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
* Pointer to the allocated workqueue on success, %NULL on failure.
*/
#define alloc_ordered_workqueue(fmt, flags, args...) \
- alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args)
+ alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | \
+ __WQ_ORDERED_EXPLICIT | (flags), 1, ##args)
#define create_workqueue(name) \
alloc_workqueue("%s", WQ_MEM_RECLAIM, 1, (name))
diff --git a/include/net/ip.h b/include/net/ip.h
index 8a8376239eac..c10f73803845 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -317,7 +317,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
!forwarding)
return dst_mtu(dst);
- return min(dst->dev->mtu, IP_MAX_MTU);
+ return min(READ_ONCE(dst->dev->mtu), IP_MAX_MTU);
}
static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb)
@@ -330,7 +330,7 @@ static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb)
return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding);
}
- return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU);
+ return min(READ_ONCE(skb_dst(skb)->dev->mtu), IP_MAX_MTU);
}
u32 ip_idents_reserve(u32 hash, int segs);
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 814a13d22df6..f9bdfb096579 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -21,6 +21,7 @@ struct route_info {
#include <net/flow.h>
#include <net/ip6_fib.h>
#include <net/sock.h>
+#include <net/lwtunnel.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/route.h>
@@ -209,4 +210,11 @@ static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt,
return daddr;
}
+static inline bool rt6_duplicate_nexthop(struct rt6_info *a, struct rt6_info *b)
+{
+ return a->dst.dev == b->dst.dev &&
+ a->rt6i_idev == b->rt6i_idev &&
+ ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) &&
+ !lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate);
+}
#endif
diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h
index e0f4109e64c6..c2aa73e5e6bb 100644
--- a/include/net/iw_handler.h
+++ b/include/net/iw_handler.h
@@ -556,7 +556,8 @@ iwe_stream_add_point(struct iw_request_info *info, char *stream, char *ends,
memcpy(stream + lcp_len,
((char *) &iwe->u) + IW_EV_POINT_OFF,
IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
- memcpy(stream + point_len, extra, iwe->u.data.length);
+ if (iwe->u.data.length && extra)
+ memcpy(stream + point_len, extra, iwe->u.data.length);
stream += event_len;
}
return stream;
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index e5bba897d206..7a5d6a073165 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -717,8 +717,11 @@ static inline struct Qdisc *qdisc_replace(struct Qdisc *sch, struct Qdisc *new,
old = *pold;
*pold = new;
if (old != NULL) {
- qdisc_tree_reduce_backlog(old, old->q.qlen, old->qstats.backlog);
+ unsigned int qlen = old->q.qlen;
+ unsigned int backlog = old->qstats.backlog;
+
qdisc_reset(old);
+ qdisc_tree_reduce_backlog(old, qlen, backlog);
}
sch_tree_unlock(sch);
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index ce13cf20f625..d33b17ba51d2 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -444,6 +444,8 @@ _sctp_walk_params((pos), (chunk), ntohs((chunk)->chunk_hdr.length), member)
#define _sctp_walk_params(pos, chunk, end, member)\
for (pos.v = chunk->member;\
+ (pos.v + offsetof(struct sctp_paramhdr, length) + sizeof(pos.p->length) <=\
+ (void *)chunk + end) &&\
pos.v <= (void *)chunk + end - ntohs(pos.p->length) &&\
ntohs(pos.p->length) >= sizeof(sctp_paramhdr_t);\
pos.v += WORD_ROUND(ntohs(pos.p->length)))
@@ -454,6 +456,8 @@ _sctp_walk_errors((err), (chunk_hdr), ntohs((chunk_hdr)->length))
#define _sctp_walk_errors(err, chunk_hdr, end)\
for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \
sizeof(sctp_chunkhdr_t));\
+ ((void *)err + offsetof(sctp_errhdr_t, length) + sizeof(err->length) <=\
+ (void *)chunk_hdr + end) &&\
(void *)err <= (void *)chunk_hdr + end - ntohs(err->length) &&\
ntohs(err->length) >= sizeof(sctp_errhdr_t); \
err = (sctp_errhdr_t *)((void *)err + WORD_ROUND(ntohs(err->length))))
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/scm.h b/include/soc/qcom/scm.h
index af389305207f..f0a3124dae00 100644
--- a/include/soc/qcom/scm.h
+++ b/include/soc/qcom/scm.h
@@ -121,9 +121,9 @@ extern s32 scm_call_atomic5_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, u32 arg3,
extern u32 scm_get_version(void);
extern int scm_is_call_available(u32 svc_id, u32 cmd_id);
-extern int scm_get_feat_version(u32 feat);
+extern int scm_get_feat_version(u32 feat, u64 *scm_ret);
extern bool is_scm_armv8(void);
-extern int scm_restore_sec_cfg(u32 device_id, u32 spare, int *scm_ret);
+extern int scm_restore_sec_cfg(u32 device_id, u32 spare, u64 *scm_ret);
extern u32 scm_io_read(phys_addr_t address);
extern int scm_io_write(phys_addr_t address, u32 val);
extern bool scm_is_secure_device(void);
@@ -205,7 +205,7 @@ static inline int scm_is_call_available(u32 svc_id, u32 cmd_id)
return 0;
}
-static inline int scm_get_feat_version(u32 feat)
+static inline int scm_get_feat_version(u32 feat, u64 *scm_ret)
{
return 0;
}
@@ -215,7 +215,7 @@ static inline bool is_scm_armv8(void)
return true;
}
-static inline int scm_restore_sec_cfg(u32 device_id, u32 spare, int *scm_ret)
+static inline int scm_restore_sec_cfg(u32 device_id, u32 spare, u64 *scm_ret)
{
return 0;
}
diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
index 2e8d71754c98..9110963d0e9f 100644
--- a/include/soc/qcom/socinfo.h
+++ b/include/soc/qcom/socinfo.h
@@ -96,6 +96,10 @@
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm660")
#define early_machine_is_sda660() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sda660")
+#define early_machine_is_sdm636() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm636")
+#define early_machine_is_sda636() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sda636")
#define early_machine_is_sdm658() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm658")
#define early_machine_is_sda658() \
@@ -142,6 +146,8 @@
#define early_machine_is_msmhamster() 0
#define early_machine_is_sdm660() 0
#define early_machine_is_sda660() 0
+#define early_machine_is_sdm636() 0
+#define early_machine_is_sda636() 0
#define early_machine_is_sdm658() 0
#define early_machine_is_sda658() 0
#define early_machine_is_sdm630() 0
@@ -206,6 +212,7 @@ enum msm_cpu {
MSM_CPU_HAMSTER,
MSM_CPU_660,
MSM_CPU_630,
+ MSM_CPU_636,
};
struct msm_soc_info {
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 2680a13721c2..21b30bf7c74c 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -4012,7 +4012,7 @@ struct asm_stream_pan_ctrl_params {
uint16_t num_input_channels;
uint16_t output_channel_map[8];
uint16_t input_channel_map[8];
- uint16_t gain[64];
+ uint32_t gain[64];
} __packed;
#define ASM_END_POINT_DEVICE_MATRIX 0
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 177c2f4da32e..9ddd02cac9ac 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -99,7 +99,7 @@
#define SOFT_PAUSE_ENABLE 1
#define SOFT_PAUSE_DISABLE 0
-#define ASM_ACTIVE_STREAMS_ALLOWED 0x8
+#define ASM_ACTIVE_STREAMS_ALLOWED 0x9
/* Control session is used for mapping calibration memory */
#define ASM_CONTROL_SESSION (ASM_ACTIVE_STREAMS_ALLOWED + 1)
@@ -639,7 +639,8 @@ int q6asm_send_audio_effects_params(struct audio_client *ac, char *params,
int q6asm_send_stream_cmd(struct audio_client *ac,
struct msm_adsp_event_data *data);
-int q6asm_send_ion_fd(struct audio_client *ac, int fd);
+int q6asm_audio_map_shm_fd(struct audio_client *ac, struct ion_client **client,
+ struct ion_handle **handle, int fd);
int q6asm_send_rtic_event_ack(struct audio_client *ac,
void *param, uint32_t params_length);
diff --git a/include/sound/q6core.h b/include/sound/q6core.h
index 1d81bda4b513..4f55880d410f 100644
--- a/include/sound/q6core.h
+++ b/include/sound/q6core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2016, 2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,8 @@
#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D
bool q6core_is_adsp_ready(void);
+int q6core_add_remove_pool_pages(phys_addr_t buf_add, uint32_t bufsz,
+ uint32_t mempool_id, bool add_pages);
#define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919
#define DTS_EAGLE_LICENSE_ID 0x00028346
@@ -153,4 +155,16 @@ struct avcs_cmd_deregister_topologies {
int32_t core_set_license(uint32_t key, uint32_t module_id);
int32_t core_get_license_status(uint32_t module_id);
+#define ADSP_MEMORY_MAP_HLOS_PHYSPOOL 4
+#define AVCS_CMD_ADD_POOL_PAGES 0x0001292E
+#define AVCS_CMD_REMOVE_POOL_PAGES 0x0001292F
+
+struct avs_mem_assign_region {
+ struct apr_hdr hdr;
+ u32 pool_id;
+ u32 size;
+ u32 addr_lsw;
+ u32 addr_msw;
+} __packed;
+
#endif /* __Q6CORE_H__ */
diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h
index 373d3342002b..22f442ab85f9 100644
--- a/include/target/iscsi/iscsi_target_core.h
+++ b/include/target/iscsi/iscsi_target_core.h
@@ -64,6 +64,14 @@
#define TA_DEFAULT_FABRIC_PROT_TYPE 0
/* TPG status needs to be enabled to return sendtargets discovery endpoint info */
#define TA_DEFAULT_TPG_ENABLED_SENDTARGETS 1
+/*
+ * Used to control the sending of keys with optional to respond state bit,
+ * as a workaround for non RFC compliant initiators,that do not propose,
+ * nor respond to specific keys required for login to complete.
+ *
+ * See iscsi_check_proposer_for_optional_reply() for more details.
+ */
+#define TA_DEFAULT_LOGIN_KEYS_WORKAROUND 1
#define ISCSI_IOV_DATA_BUFFER 5
@@ -554,6 +562,7 @@ struct iscsi_conn {
#define LOGIN_FLAGS_READ_ACTIVE 1
#define LOGIN_FLAGS_CLOSED 2
#define LOGIN_FLAGS_READY 4
+#define LOGIN_FLAGS_INITIAL_PDU 8
unsigned long login_flags;
struct delayed_work login_work;
struct delayed_work login_cleanup_work;
@@ -765,6 +774,7 @@ struct iscsi_tpg_attrib {
u8 t10_pi;
u32 fabric_prot_type;
u32 tpg_enabled_sendtargets;
+ u32 login_keys_workaround;
struct iscsi_portal_group *tpg;
};
@@ -774,6 +784,7 @@ struct iscsi_np {
int np_sock_type;
enum np_thread_state_table np_thread_state;
bool enabled;
+ atomic_t np_reset_count;
enum iscsi_timer_flags_table np_login_timer_flags;
u32 np_exports;
enum np_flags_table np_flags;
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index ed66414b91f0..1adf8739980c 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -714,6 +714,7 @@ struct se_lun {
#define SE_LUN_LINK_MAGIC 0xffff7771
u32 lun_link_magic;
u32 lun_access;
+ bool lun_shutdown;
u32 lun_index;
/* RELATIVE TARGET PORT IDENTIFER */
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 84f7f2139a91..feffec7b489a 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -1698,6 +1698,48 @@ TRACE_EVENT(sched_boost_task,
);
/*
+ * Tracepoint for find_best_target
+ */
+TRACE_EVENT(sched_find_best_target,
+
+ TP_PROTO(struct task_struct *tsk, bool prefer_idle,
+ unsigned long min_util, int start_cpu,
+ int best_idle, int best_active, int target),
+
+ TP_ARGS(tsk, prefer_idle, min_util, start_cpu,
+ best_idle, best_active, target),
+
+ TP_STRUCT__entry(
+ __array( char, comm, TASK_COMM_LEN )
+ __field( pid_t, pid )
+ __field( unsigned long, min_util )
+ __field( bool, prefer_idle )
+ __field( int, start_cpu )
+ __field( int, best_idle )
+ __field( int, best_active )
+ __field( int, target )
+ ),
+
+ TP_fast_assign(
+ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
+ __entry->pid = tsk->pid;
+ __entry->min_util = min_util;
+ __entry->prefer_idle = prefer_idle;
+ __entry->start_cpu = start_cpu;
+ __entry->best_idle = best_idle;
+ __entry->best_active = best_active;
+ __entry->target = target;
+ ),
+
+ TP_printk("pid=%d comm=%s prefer_idle=%d start_cpu=%d "
+ "best_idle=%d best_active=%d target=%d",
+ __entry->pid, __entry->comm,
+ __entry->prefer_idle, __entry->start_cpu,
+ __entry->best_idle, __entry->best_active,
+ __entry->target)
+);
+
+/*
* Tracepoint for accounting sched group energy
*/
TRACE_EVENT(sched_energy_diff,
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h
index 8b51873e7b08..a852f2a3701f 100644
--- a/include/uapi/drm/msm_drm.h
+++ b/include/uapi/drm/msm_drm.h
@@ -234,7 +234,7 @@ struct drm_msm_gem_submit_cmd {
__u32 size; /* in, cmdstream size */
__u32 pad;
__u32 nr_relocs; /* in, number of submit_reloc's */
- __u64 __user relocs; /* in, ptr to array of submit_reloc's */
+ __u64 relocs; /* in, ptr to array of submit_reloc's */
};
/* Each buffer referenced elsewhere in the cmdstream submit (ie. the
@@ -274,8 +274,8 @@ struct drm_msm_gem_submit {
__u32 fence; /* out */
__u32 nr_bos; /* in, number of submit_bo's */
__u32 nr_cmds; /* in, number of submit_cmd's */
- __u64 __user bos; /* in, ptr to array of submit_bo's */
- __u64 __user cmds; /* in, ptr to array of submit_cmd's */
+ __u64 bos; /* in, ptr to array of submit_bo's */
+ __u64 cmds; /* in, ptr to array of submit_cmd's */
__s32 fence_fd; /* gap for the fence_fd which is upstream */
__u32 queueid; /* in, submitqueue id */
};
diff --git a/include/uapi/drm/sde_drm.h b/include/uapi/drm/sde_drm.h
index bef841446247..71159cb377d8 100644
--- a/include/uapi/drm/sde_drm.h
+++ b/include/uapi/drm/sde_drm.h
@@ -337,4 +337,14 @@ struct sde_drm_wb_cfg {
uint64_t modes;
};
+/**
+ * Define extended power modes supported by the SDE connectors.
+ */
+#define SDE_MODE_DPMS_ON 0
+#define SDE_MODE_DPMS_LP1 1
+#define SDE_MODE_DPMS_LP2 2
+#define SDE_MODE_DPMS_STANDBY 3
+#define SDE_MODE_DPMS_SUSPEND 4
+#define SDE_MODE_DPMS_OFF 5
+
#endif /* _SDE_DRM_H_ */
diff --git a/include/uapi/linux/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/android/binder.h b/include/uapi/linux/android/binder.h
index 7668b5791c91..5539933b3491 100644
--- a/include/uapi/linux/android/binder.h
+++ b/include/uapi/linux/android/binder.h
@@ -37,9 +37,56 @@ enum {
BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};
-enum {
+/**
+ * enum flat_binder_object_shifts: shift values for flat_binder_object_flags
+ * @FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT: shift for getting scheduler policy.
+ *
+ */
+enum flat_binder_object_shifts {
+ FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT = 9,
+};
+
+/**
+ * enum flat_binder_object_flags - flags for use in flat_binder_object.flags
+ */
+enum flat_binder_object_flags {
+ /**
+ * @FLAT_BINDER_FLAG_PRIORITY_MASK: bit-mask for min scheduler priority
+ *
+ * These bits can be used to set the minimum scheduler priority
+ * at which transactions into this node should run. Valid values
+ * in these bits depend on the scheduler policy encoded in
+ * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK.
+ *
+ * For SCHED_NORMAL/SCHED_BATCH, the valid range is between [-20..19]
+ * For SCHED_FIFO/SCHED_RR, the value can run between [1..99]
+ */
FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
+ /**
+ * @FLAT_BINDER_FLAG_ACCEPTS_FDS: whether the node accepts fds.
+ */
FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
+ /**
+ * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK: bit-mask for scheduling policy
+ *
+ * These two bits can be used to set the min scheduling policy at which
+ * transactions on this node should run. These match the UAPI
+ * scheduler policy values, eg:
+ * 00b: SCHED_NORMAL
+ * 01b: SCHED_FIFO
+ * 10b: SCHED_RR
+ * 11b: SCHED_BATCH
+ */
+ FLAT_BINDER_FLAG_SCHED_POLICY_MASK =
+ 3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT,
+
+ /**
+ * @FLAT_BINDER_FLAG_INHERIT_RT: whether the node inherits RT policy
+ *
+ * Only when set, calls into this node will inherit a real-time
+ * scheduling policy from the caller (for synchronous transactions).
+ */
+ FLAT_BINDER_FLAG_INHERIT_RT = 0x800,
};
#ifdef BINDER_IPC_32BIT
@@ -186,6 +233,19 @@ struct binder_version {
#define BINDER_CURRENT_PROTOCOL_VERSION 8
#endif
+/*
+ * Use with BINDER_GET_NODE_DEBUG_INFO, driver reads ptr, writes to all fields.
+ * Set ptr to NULL for the first call to get the info for the first node, and
+ * then repeat the call passing the previously returned value to get the next
+ * nodes. ptr will be 0 when there are no more nodes.
+ */
+struct binder_node_debug_info {
+ binder_uintptr_t ptr;
+ binder_uintptr_t cookie;
+ __u32 has_strong_ref;
+ __u32 has_weak_ref;
+};
+
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
@@ -193,6 +253,7 @@ struct binder_version {
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32)
#define BINDER_THREAD_EXIT _IOW('b', 8, __s32)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
+#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info)
/*
* NOTE: Two special error codes you should check for when calling
diff --git a/include/uapi/linux/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/ipa_qmi_service_v01.h b/include/uapi/linux/ipa_qmi_service_v01.h
index 60867630e1a1..dc46ee0f29a2 100644
--- a/include/uapi/linux/ipa_qmi_service_v01.h
+++ b/include/uapi/linux/ipa_qmi_service_v01.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -47,6 +47,12 @@
#define QMI_IPA_MAX_FILTERS_EX_V01 128
#define QMI_IPA_MAX_PIPES_V01 20
#define QMI_IPA_MAX_APN_V01 8
+#define QMI_IPA_MAX_PER_CLIENTS_V01 64
+/* Currently max we can use is only 1. But for scalability purpose
+ * we are having max value as 8.
+ */
+#define QMI_IPA_MAX_CLIENT_DST_PIPES_V01 8
+#define QMI_IPA_MAX_UL_FIREWALL_RULES_V01 64
#define IPA_INT_MAX ((int)(~0U>>1))
#define IPA_INT_MIN (-IPA_INT_MAX - 1)
@@ -984,6 +990,16 @@ struct ipa_fltr_installed_notif_req_msg_v01 {
* failure, the Rule Ids in this list must be set to a reserved
* index (255).
*/
+
+ /* Optional */
+ /* List of destination pipe IDs. */
+ uint8_t dst_pipe_id_valid;
+ /* Must be set to true if dst_pipe_id is being passed. */
+ uint32_t dst_pipe_id_len;
+ /* Must be set to # of elements in dst_pipe_id. */
+ uint32_t dst_pipe_id[QMI_IPA_MAX_CLIENT_DST_PIPES_V01];
+ /* Provides the list of destination pipe IDs for a source pipe. */
+
}; /* Message */
/* Response Message; This is the message that is exchanged between the
@@ -1622,6 +1638,273 @@ struct ipa_install_fltr_rule_resp_ex_msg_v01 {
*/
}; /* Message */
+/*
+ * Request Message; Requests the modem IPA driver to enable or
+ * disable collection of per client statistics.
+ */
+struct ipa_enable_per_client_stats_req_msg_v01 {
+
+ /* Mandatory */
+ /* Collect statistics per client; */
+ uint8_t enable_per_client_stats;
+ /*
+ * Indicates whether to start or stop collecting
+ * per client statistics.
+ */
+}; /* Message */
+
+/*
+ * Response Message; Requests the modem IPA driver to enable or disable
+ * collection of per client statistics.
+ */
+struct ipa_enable_per_client_stats_resp_msg_v01 {
+
+ /* Mandatory */
+ /* Result Code */
+ struct ipa_qmi_response_type_v01 resp;
+ /* Standard response type. */
+}; /* Message */
+
+struct ipa_per_client_stats_info_type_v01 {
+
+ uint32_t client_id;
+ /*
+ * Id of the client on APPS processor side for which Modem processor
+ * needs to send uplink/downlink statistics.
+ */
+
+ uint32_t src_pipe_id;
+ /*
+ * IPA consumer pipe on which client on APPS side sent uplink
+ * data to modem.
+ */
+
+ uint64_t num_ul_ipv4_bytes;
+ /*
+ * Accumulated number of uplink IPv4 bytes for a client.
+ */
+
+ uint64_t num_ul_ipv6_bytes;
+ /*
+ * Accumulated number of uplink IPv6 bytes for a client.
+ */
+
+ uint64_t num_dl_ipv4_bytes;
+ /*
+ * Accumulated number of downlink IPv4 bytes for a client.
+ */
+
+ uint64_t num_dl_ipv6_bytes;
+ /*
+ * Accumulated number of downlink IPv6 byes for a client.
+ */
+
+
+ uint32_t num_ul_ipv4_pkts;
+ /*
+ * Accumulated number of uplink IPv4 packets for a client.
+ */
+
+ uint32_t num_ul_ipv6_pkts;
+ /*
+ * Accumulated number of uplink IPv6 packets for a client.
+ */
+
+ uint32_t num_dl_ipv4_pkts;
+ /*
+ * Accumulated number of downlink IPv4 packets for a client.
+ */
+
+ uint32_t num_dl_ipv6_pkts;
+ /*
+ * Accumulated number of downlink IPv6 packets for a client.
+ */
+}; /* Type */
+
+/*
+ * Request Message; Requests the modem IPA driver to provide statistics
+ * for a givenclient.
+ */
+struct ipa_get_stats_per_client_req_msg_v01 {
+
+ /* Mandatory */
+ /* Client id */
+ uint32_t client_id;
+ /*
+ * Id of the client on APPS processor side for which Modem processor
+ * needs to send uplink/downlink statistics. if client id is specified
+ * as 0xffffffff, then Q6 will send the stats for all the clients of
+ * the specified source pipe.
+ */
+
+ /* Mandatory */
+ /* Source pipe id */
+ uint32_t src_pipe_id;
+ /*
+ * IPA consumer pipe on which client on APPS side sent uplink
+ * data to modem. In future, this implementation can be extended
+ * to provide 0xffffffff as the source pipe id, where Q6 will send
+ * the stats of all the clients across all different tethered-pipes.
+ */
+
+ /* Optional */
+ /* Reset client statistics. */
+ uint8_t reset_stats_valid;
+ /* Must be set to true if reset_stats is being passed. */
+ uint8_t reset_stats;
+ /*
+ * Option to reset the statistics currently collected by modem for this
+ * particular client.
+ */
+}; /* Message */
+
+/*
+ * Response Message; Requests the modem IPA driver to provide statistics
+ * for a given client.
+ */
+struct ipa_get_stats_per_client_resp_msg_v01 {
+
+ /* Mandatory */
+ /* Result Code */
+ struct ipa_qmi_response_type_v01 resp;
+ /* Standard response type. */
+
+ /* Optional */
+ /* Per clients Statistics List */
+ uint8_t per_client_stats_list_valid;
+ /* Must be set to true if per_client_stats_list is being passed. */
+ uint32_t per_client_stats_list_len;
+ /* Must be set to # of elements in per_client_stats_list. */
+ struct ipa_per_client_stats_info_type_v01
+ per_client_stats_list[QMI_IPA_MAX_PER_CLIENTS_V01];
+ /*
+ * List of all per client statistics that are retrieved.
+ */
+}; /* Message */
+
+struct ipa_ul_firewall_rule_type_v01 {
+
+ enum ipa_ip_type_enum_v01 ip_type;
+ /*
+ * IP type for which this rule is applicable.
+ * The driver must identify the filter table (v6 or v4), and this
+ * field is essential for that. Values:
+ * - QMI_IPA_IP_TYPE_INVALID (0) -- Invalid IP type identifier
+ * - QMI_IPA_IP_TYPE_V4 (1) -- IPv4 type
+ * - QMI_IPA_IP_TYPE_V6 (2) -- IPv6 type
+ */
+
+ struct ipa_filter_rule_type_v01 filter_rule;
+ /*
+ * Rules in the filter specification. These rules are the
+ * ones that are matched against fields in the packet.
+ * Currently we only send IPv6 whitelist rules to Q6.
+ */
+}; /* Type */
+
+/*
+ * Request Message; Requestes remote IPA driver to install uplink
+ * firewall rules.
+ */
+struct ipa_configure_ul_firewall_rules_req_msg_v01 {
+
+ /* Optional */
+ /* Uplink Firewall Specification */
+ uint32_t firewall_rules_list_len;
+ /* Must be set to # of elements in firewall_rules_list. */
+ struct ipa_ul_firewall_rule_type_v01
+ firewall_rules_list[QMI_IPA_MAX_UL_FIREWALL_RULES_V01];
+ /*
+ * List of uplink firewall specifications of filters that must be
+ * installed.
+ */
+
+ uint32_t mux_id;
+ /*
+ * QMAP Mux ID. As a part of the QMAP protocol,
+ * several data calls may be multiplexed over the same physical
+ * transport channel. This identifier is used to identify one
+ * such data call. The maximum value for this identifier is 255.
+ */
+
+ /* Optional */
+ uint8_t disable_valid;
+ /* Must be set to true if enable is being passed. */
+ uint8_t disable;
+ /*
+ * Indicates whether uplink firewall needs to be enabled or disabled.
+ */
+
+ /* Optional */
+ uint8_t are_blacklist_filters_valid;
+ /* Must be set to true if are_blacklist_filters is being passed. */
+ uint8_t are_blacklist_filters;
+ /*
+ * Indicates whether the filters received as part of this message are
+ * blacklist filters. i.e. drop uplink packets matching these rules.
+ */
+}; /* Message */
+
+/*
+ * Response Message; Requestes remote IPA driver to install
+ * uplink firewall rules.
+ */
+struct ipa_configure_ul_firewall_rules_resp_msg_v01 {
+
+ /* Mandatory */
+ /* Result Code */
+ struct ipa_qmi_response_type_v01 resp;
+ /*
+ * Standard response type.
+ * Standard response type. Contains the following data members:
+ * qmi_result_type -- QMI_RESULT_SUCCESS or QMI_RESULT_FAILURE
+ * qmi_error_type -- Error code. Possible error code values are
+ * described in the error codes section of each message definition.
+ */
+}; /* Message */
+
+enum ipa_ul_firewall_status_enum_v01 {
+ IPA_UL_FIREWALL_STATUS_ENUM_MIN_ENUM_VAL_V01 = -2147483647,
+ /* To force a 32 bit signed enum. Do not change or use*/
+ QMI_IPA_UL_FIREWALL_STATUS_SUCCESS_V01 = 0,
+ /* Indicates that the uplink firewall rules
+ * are configured successfully.
+ */
+ QMI_IPA_UL_FIREWALL_STATUS_FAILURE_V01 = 1,
+ /* Indicates that the uplink firewall rules
+ * are not configured successfully.
+ */
+ IPA_UL_FIREWALL_STATUS_ENUM_MAX_ENUM_VAL_V01 = 2147483647
+ /* To force a 32 bit signed enum. Do not change or use*/
+};
+
+struct ipa_ul_firewall_config_result_type_v01 {
+
+ enum ipa_ul_firewall_status_enum_v01 is_success;
+ /*
+ * Indicates whether the uplink firewall rules are configured
+ * successfully.
+ */
+
+ uint32_t mux_id;
+ /*
+ * QMAP Mux ID. As a part of the QMAP protocol,
+ * several data calls may be multiplexed over the same physical
+ * transport channel. This identifier is used to identify one
+ * such data call. The maximum value for this identifier is 255.
+ */
+};
+
+/*
+ * Indication Message; Requestes remote IPA driver to install
+ * uplink firewall rules.
+ */
+struct ipa_configure_ul_firewall_rules_ind_msg_v01 {
+
+ struct ipa_ul_firewall_config_result_type_v01 result;
+}; /* Message */
+
+
/*Service Message Definition*/
#define QMI_IPA_INDICATION_REGISTER_REQ_V01 0x0020
#define QMI_IPA_INDICATION_REGISTER_RESP_V01 0x0020
@@ -1655,6 +1938,13 @@ struct ipa_install_fltr_rule_resp_ex_msg_v01 {
#define QMI_IPA_INIT_MODEM_DRIVER_CMPLT_RESP_V01 0x0035
#define QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_V01 0x0037
#define QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_V01 0x0037
+#define QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_V01 0x0038
+#define QMI_IPA_ENABLE_PER_CLIENT_STATS_RESP_V01 0x0038
+#define QMI_IPA_GET_STATS_PER_CLIENT_REQ_V01 0x0039
+#define QMI_IPA_GET_STATS_PER_CLIENT_RESP_V01 0x0039
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_V01 0x003A
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_RESP_V01 0x003A
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_V01 0x003A
/* add for max length*/
#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 134
@@ -1663,7 +1953,7 @@ struct ipa_install_fltr_rule_resp_ex_msg_v01 {
#define QMI_IPA_INDICATION_REGISTER_RESP_MAX_MSG_LEN_V01 7
#define QMI_IPA_INSTALL_FILTER_RULE_REQ_MAX_MSG_LEN_V01 22369
#define QMI_IPA_INSTALL_FILTER_RULE_RESP_MAX_MSG_LEN_V01 783
-#define QMI_IPA_FILTER_INSTALLED_NOTIF_REQ_MAX_MSG_LEN_V01 834
+#define QMI_IPA_FILTER_INSTALLED_NOTIF_REQ_MAX_MSG_LEN_V01 870
#define QMI_IPA_FILTER_INSTALLED_NOTIF_RESP_MAX_MSG_LEN_V01 7
#define QMI_IPA_MASTER_DRIVER_INIT_COMPLETE_IND_MAX_MSG_LEN_V01 7
#define QMI_IPA_DATA_USAGE_QUOTA_REACHED_IND_MAX_MSG_LEN_V01 15
@@ -1696,6 +1986,15 @@ struct ipa_install_fltr_rule_resp_ex_msg_v01 {
#define QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_MAX_MSG_LEN_V01 22685
#define QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_MAX_MSG_LEN_V01 523
+#define QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_MAX_MSG_LEN_V01 4
+#define QMI_IPA_ENABLE_PER_CLIENT_STATS_RESP_MAX_MSG_LEN_V01 7
+
+#define QMI_IPA_GET_STATS_PER_CLIENT_REQ_MAX_MSG_LEN_V01 18
+#define QMI_IPA_GET_STATS_PER_CLIENT_RESP_MAX_MSG_LEN_V01 3595
+
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_MAX_MSG_LEN_V01 9875
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_RESP_MAX_MSG_LEN_V01 7
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_MAX_MSG_LEN_V01 11
/* Service Object Accessor */
#endif/* IPA_QMI_SERVICE_V01_H */
diff --git a/include/uapi/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/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h
index 83927c614e91..a92c144f712e 100644
--- a/include/uapi/media/msm_camsensor_sdk.h
+++ b/include/uapi/media/msm_camsensor_sdk.h
@@ -113,8 +113,10 @@ enum msm_sensor_power_seq_gpio_t {
SENSOR_GPIO_FL_RESET,
SENSOR_GPIO_CUSTOM1,
SENSOR_GPIO_CUSTOM2,
+ SENSOR_GPIO_CUSTOM3,
SENSOR_GPIO_MAX,
};
+#define SENSOR_GPIO_CUSTOM3 SENSOR_GPIO_CUSTOM3
enum msm_ir_cut_filter_gpio_t {
IR_CUT_FILTER_GPIO_P = 0,
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h
index 866ec3d2af69..7845fdd556fa 100644
--- a/include/uapi/sound/compress_offload.h
+++ b/include/uapi/sound/compress_offload.h
@@ -138,6 +138,11 @@ struct snd_compr_audio_info {
#define SNDRV_COMPRESS_CLK_REC_MODE_NONE 0
#define SNDRV_COMPRESS_CLK_REC_MODE_AUTO 1
+enum sndrv_compress_latency_mode {
+ SNDRV_COMPRESS_LEGACY_LATENCY_MODE = 0,
+ SNDRV_COMPRESS_LOW_LATENCY_MODE = 1,
+};
+
/**
* enum sndrv_compress_encoder
* @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the
@@ -164,6 +169,7 @@ enum sndrv_compress_encoder {
SNDRV_COMPRESS_START_DELAY = 9,
SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK = 10,
SNDRV_COMPRESS_ADJUST_SESSION_CLOCK = 11,
+ SNDRV_COMPRESS_LATENCY_MODE = 12,
};
#define SNDRV_COMPRESS_PATH_DELAY SNDRV_COMPRESS_PATH_DELAY
@@ -174,6 +180,7 @@ enum sndrv_compress_encoder {
#define SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK \
SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK
#define SNDRV_COMPRESS_ADJUST_SESSION_CLOCK SNDRV_COMPRESS_ADJUST_SESSION_CLOCK
+#define SNDRV_COMPRESS_LATENCY_MODE SNDRV_COMPRESS_LATENCY_MODE
/**
* struct snd_compr_metadata - compressed stream metadata
diff --git a/init/Kconfig b/init/Kconfig
index 7b8b4171ce00..1473414ec8fe 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -26,6 +26,16 @@ config IRQ_WORK
config BUILDTIME_EXTABLE_SORT
bool
+config THREAD_INFO_IN_TASK
+ bool
+ help
+ Select this to move thread_info off the stack into task_struct. To
+ make this work, an arch will need to remove all thread_info fields
+ except flags and fix any runtime bugs.
+
+ One subtle change that will be needed is to use try_get_task_stack()
+ and put_task_stack() in save_thread_stack_tsk() and get_wchan().
+
menu "General setup"
config BROKEN
diff --git a/init/init_task.c b/init/init_task.c
index ba0a7f362d9e..11f83be1fa79 100644
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -22,5 +22,8 @@ EXPORT_SYMBOL(init_task);
* Initial thread structure. Alignment of this is handled by a special
* linker map entry.
*/
-union thread_union init_thread_union __init_task_data =
- { INIT_THREAD_INFO(init_task) };
+union thread_union init_thread_union __init_task_data = {
+#ifndef CONFIG_THREAD_INFO_IN_TASK
+ INIT_THREAD_INFO(init_task)
+#endif
+};
diff --git a/init/main.c b/init/main.c
index 7d4532bff5da..8c72af285838 100644
--- a/init/main.c
+++ b/init/main.c
@@ -468,7 +468,7 @@ void __init __weak smp_setup_processor_id(void)
}
# if THREAD_SIZE >= PAGE_SIZE
-void __init __weak thread_info_cache_init(void)
+void __init __weak thread_stack_cache_init(void)
{
}
#endif
@@ -644,7 +644,7 @@ asmlinkage __visible void __init start_kernel(void)
/* Should be run before the first non-init thread is created */
init_espfix_bsp();
#endif
- thread_info_cache_init();
+ thread_stack_cache_init();
cred_init();
fork_init();
proc_caches_init();
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 25b7a678f9ef..46436543ad28 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -1251,8 +1251,10 @@ retry:
timeo = MAX_SCHEDULE_TIMEOUT;
ret = netlink_attachskb(sock, nc, &timeo, NULL);
- if (ret == 1)
+ if (ret == 1) {
+ sock = NULL;
goto retry;
+ }
if (ret) {
sock = NULL;
nc = NULL;
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index 939945a5649c..a162661c9d60 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -457,13 +457,15 @@ void audit_remove_watch_rule(struct audit_krule *krule)
list_del(&krule->rlist);
if (list_empty(&watch->rules)) {
+ /*
+ * audit_remove_watch() drops our reference to 'parent' which
+ * can get freed. Grab our own reference to be safe.
+ */
+ audit_get_parent(parent);
audit_remove_watch(watch);
-
- if (list_empty(&parent->watches)) {
- audit_get_parent(parent);
+ if (list_empty(&parent->watches))
fsnotify_destroy_mark(&parent->mark, audit_watch_group);
- audit_put_parent(parent);
- }
+ audit_put_parent(parent);
}
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 85de5094b936..c97bce6a0e0e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -765,6 +765,11 @@ static int check_xadd(struct verifier_env *env, struct bpf_insn *insn)
if (err)
return err;
+ if (is_pointer_value(env, insn->src_reg)) {
+ verbose("R%d leaks addr into mem\n", insn->src_reg);
+ return -EACCES;
+ }
+
/* check whether atomic_add can read the memory */
err = check_mem_access(env, insn->dst_reg, insn->off,
BPF_SIZE(insn->code), BPF_READ, -1);
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index 03dbc231a4a0..6bdfe995e009 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -60,6 +60,7 @@
#include <linux/cgroup.h>
#include <linux/wait.h>
+struct static_key cpusets_pre_enable_key __read_mostly = STATIC_KEY_INIT_FALSE;
struct static_key cpusets_enabled_key __read_mostly = STATIC_KEY_INIT_FALSE;
/* See "Frequency meter" comments, below. */
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 7fee87daac56..98928fb7fecc 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1693,7 +1693,33 @@ static int __perf_remove_from_context(void *info)
return 0;
}
-/*
+
+#ifdef CONFIG_SMP
+static void perf_retry_remove(struct perf_event *event,
+ struct remove_event *rep)
+{
+ int up_ret;
+ /*
+ * CPU was offline. Bring it online so we can
+ * gracefully exit a perf context.
+ */
+ up_ret = cpu_up(event->cpu);
+ if (!up_ret)
+ /* Try the remove call once again. */
+ cpu_function_call(event->cpu, __perf_remove_from_context,
+ rep);
+ else
+ pr_err("Failed to bring up CPU: %d, ret: %d\n",
+ event->cpu, up_ret);
+}
+#else
+static void perf_retry_remove(struct perf_event *event,
+ struct remove_event *rep)
+{
+}
+#endif
+
+ /*
* Remove the event from a task's (or a CPU's) list of events.
*
* CPU events are removed with a smp call. For task events we only
@@ -1728,6 +1754,9 @@ static void __ref perf_remove_from_context(struct perf_event *event,
*/
ret = cpu_function_call(event->cpu, __perf_remove_from_context,
&re);
+ if (ret == -ENXIO)
+ perf_retry_remove(event, &re);
+
return;
}
@@ -3408,22 +3437,27 @@ u64 perf_event_read_local(struct perf_event *event)
static int perf_event_read(struct perf_event *event, bool group)
{
- int ret = 0;
+ int event_cpu, ret = 0;
/*
* If event is enabled and currently active on a CPU, update the
* value in the event structure:
*/
+ event_cpu = READ_ONCE(event->oncpu);
+
if (event->state == PERF_EVENT_STATE_ACTIVE &&
- !cpu_isolated(event->oncpu)) {
+ !cpu_isolated(event_cpu)) {
struct perf_read_data data = {
.event = event,
.group = group,
.ret = 0,
};
+
+ if ((unsigned int)event_cpu >= nr_cpu_ids)
+ return 0;
if (!event->attr.exclude_idle ||
- !per_cpu(is_idle, event->oncpu)) {
- smp_call_function_single(event->oncpu,
+ !per_cpu(is_idle, event_cpu)) {
+ smp_call_function_single(event_cpu,
__perf_event_read, &data, 1);
ret = data.ret;
}
@@ -6566,21 +6600,6 @@ static void perf_log_itrace_start(struct perf_event *event)
perf_output_end(&handle);
}
-static bool sample_is_allowed(struct perf_event *event, struct pt_regs *regs)
-{
- /*
- * Due to interrupt latency (AKA "skid"), we may enter the
- * kernel before taking an overflow, even if the PMU is only
- * counting user events.
- * To avoid leaking information to userspace, we must always
- * reject kernel samples when exclude_kernel is set.
- */
- if (event->attr.exclude_kernel && !user_mode(regs))
- return false;
-
- return true;
-}
-
/*
* Generic event overflow handling, sampling.
*/
@@ -6628,12 +6647,6 @@ static int __perf_event_overflow(struct perf_event *event,
}
/*
- * For security, drop the skid kernel samples if necessary.
- */
- if (!sample_is_allowed(event, regs))
- return ret;
-
- /*
* XXX event_limit might not quite work as expected on inherited
* events
*/
@@ -7109,6 +7122,8 @@ static struct pmu perf_swevent = {
.start = perf_swevent_start,
.stop = perf_swevent_stop,
.read = perf_swevent_read,
+
+ .events_across_hotplug = 1,
};
#ifdef CONFIG_EVENT_TRACING
@@ -7230,6 +7245,8 @@ static struct pmu perf_tracepoint = {
.start = perf_swevent_start,
.stop = perf_swevent_stop,
.read = perf_swevent_read,
+
+ .events_across_hotplug = 1,
};
static inline void perf_tp_register(void)
@@ -7517,6 +7534,8 @@ static struct pmu perf_cpu_clock = {
.start = cpu_clock_event_start,
.stop = cpu_clock_event_stop,
.read = cpu_clock_event_read,
+
+ .events_across_hotplug = 1,
};
/*
@@ -7598,6 +7617,8 @@ static struct pmu perf_task_clock = {
.start = task_clock_event_start,
.stop = task_clock_event_stop,
.read = task_clock_event_read,
+
+ .events_across_hotplug = 1,
};
static void perf_pmu_nop_void(struct pmu *pmu)
@@ -8677,28 +8698,27 @@ SYSCALL_DEFINE5(perf_event_open,
goto err_context;
/*
- * Do not allow to attach to a group in a different
- * task or CPU context:
+ * Make sure we're both events for the same CPU;
+ * grouping events for different CPUs is broken; since
+ * you can never concurrently schedule them anyhow.
*/
- if (move_group) {
- /*
- * Make sure we're both on the same task, or both
- * per-cpu events.
- */
- if (group_leader->ctx->task != ctx->task)
- goto err_context;
+ if (group_leader->cpu != event->cpu)
+ goto err_context;
- /*
- * Make sure we're both events for the same CPU;
- * grouping events for different CPUs is broken; since
- * you can never concurrently schedule them anyhow.
- */
- if (group_leader->cpu != event->cpu)
- goto err_context;
- } else {
- if (group_leader->ctx != ctx)
- goto err_context;
- }
+ /*
+ * Make sure we're both on the same task, or both
+ * per-CPU events.
+ */
+ if (group_leader->ctx->task != ctx->task)
+ goto err_context;
+
+ /*
+ * Do not allow to attach to a group in a different task
+ * or CPU context. If we're moving SW events, we'll fix
+ * this up later, so allow that.
+ */
+ if (!move_group && group_leader->ctx != ctx)
+ goto err_context;
/*
* Only a group leader can be exclusive or pinned
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c
index 92ce5f4ccc26..7da5b674d16e 100644
--- a/kernel/events/hw_breakpoint.c
+++ b/kernel/events/hw_breakpoint.c
@@ -614,6 +614,8 @@ static struct pmu perf_breakpoint = {
.start = hw_breakpoint_start,
.stop = hw_breakpoint_stop,
.read = hw_breakpoint_pmu_read,
+
+ .events_across_hotplug = 1,
};
int __init init_hw_breakpoint(void)
diff --git a/kernel/extable.c b/kernel/extable.c
index e820ccee9846..4f06fc34313f 100644
--- a/kernel/extable.c
+++ b/kernel/extable.c
@@ -66,7 +66,7 @@ static inline int init_kernel_text(unsigned long addr)
return 0;
}
-int core_kernel_text(unsigned long addr)
+int notrace core_kernel_text(unsigned long addr)
{
if (addr >= (unsigned long)_stext &&
addr < (unsigned long)_etext)
diff --git a/kernel/fork.c b/kernel/fork.c
index 246b8a57a32d..07cd0d68ee02 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -148,18 +148,18 @@ static inline void free_task_struct(struct task_struct *tsk)
}
#endif
-void __weak arch_release_thread_info(struct thread_info *ti)
+void __weak arch_release_thread_stack(unsigned long *stack)
{
}
-#ifndef CONFIG_ARCH_THREAD_INFO_ALLOCATOR
+#ifndef CONFIG_ARCH_THREAD_STACK_ALLOCATOR
/*
* Allocate pages if THREAD_SIZE is >= PAGE_SIZE, otherwise use a
* kmemcache based allocator.
*/
# if THREAD_SIZE >= PAGE_SIZE
-static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
+static unsigned long *alloc_thread_stack_node(struct task_struct *tsk,
int node)
{
struct page *page = alloc_kmem_pages_node(node, THREADINFO_GFP,
@@ -168,30 +168,32 @@ static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
return page ? page_address(page) : NULL;
}
-static inline void free_thread_info(struct thread_info *ti)
+static inline void free_thread_stack(unsigned long *stack)
{
- kasan_alloc_pages(virt_to_page(ti), THREAD_SIZE_ORDER);
- free_kmem_pages((unsigned long)ti, THREAD_SIZE_ORDER);
+ struct page *page = virt_to_page(stack);
+
+ kasan_alloc_pages(page, THREAD_SIZE_ORDER);
+ __free_kmem_pages(page, THREAD_SIZE_ORDER);
}
# else
-static struct kmem_cache *thread_info_cache;
+static struct kmem_cache *thread_stack_cache;
-static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
+static struct thread_info *alloc_thread_stack_node(struct task_struct *tsk,
int node)
{
- return kmem_cache_alloc_node(thread_info_cache, THREADINFO_GFP, node);
+ return kmem_cache_alloc_node(thread_stack_cache, THREADINFO_GFP, node);
}
-static void free_thread_info(struct thread_info *ti)
+static void free_stack(unsigned long *stack)
{
- kmem_cache_free(thread_info_cache, ti);
+ kmem_cache_free(thread_stack_cache, stack);
}
-void thread_info_cache_init(void)
+void thread_stack_cache_init(void)
{
- thread_info_cache = kmem_cache_create("thread_info", THREAD_SIZE,
+ thread_stack_cache = kmem_cache_create("thread_stack", THREAD_SIZE,
THREAD_SIZE, 0, NULL);
- BUG_ON(thread_info_cache == NULL);
+ BUG_ON(thread_stack_cache == NULL);
}
# endif
#endif
@@ -214,9 +216,9 @@ struct kmem_cache *vm_area_cachep;
/* SLAB cache for mm_struct structures (tsk->mm) */
static struct kmem_cache *mm_cachep;
-static void account_kernel_stack(struct thread_info *ti, int account)
+static void account_kernel_stack(unsigned long *stack, int account)
{
- struct zone *zone = page_zone(virt_to_page(ti));
+ struct zone *zone = page_zone(virt_to_page(stack));
mod_zone_page_state(zone, NR_KERNEL_STACK, account);
}
@@ -224,8 +226,8 @@ static void account_kernel_stack(struct thread_info *ti, int account)
void free_task(struct task_struct *tsk)
{
account_kernel_stack(tsk->stack, -1);
- arch_release_thread_info(tsk->stack);
- free_thread_info(tsk->stack);
+ arch_release_thread_stack(tsk->stack);
+ free_thread_stack(tsk->stack);
rt_mutex_debug_task_free(tsk);
ftrace_graph_exit_task(tsk);
put_seccomp_filter(tsk);
@@ -336,7 +338,7 @@ void set_task_stack_end_magic(struct task_struct *tsk)
static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
{
struct task_struct *tsk;
- struct thread_info *ti;
+ unsigned long *stack;
int err;
if (node == NUMA_NO_NODE)
@@ -345,15 +347,15 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
if (!tsk)
return NULL;
- ti = alloc_thread_info_node(tsk, node);
- if (!ti)
+ stack = alloc_thread_stack_node(tsk, node);
+ if (!stack)
goto free_tsk;
err = arch_dup_task_struct(tsk, orig);
if (err)
- goto free_ti;
+ goto free_stack;
- tsk->stack = ti;
+ tsk->stack = stack;
#ifdef CONFIG_SECCOMP
/*
* We must handle setting up seccomp filters once we're under
@@ -385,12 +387,12 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
tsk->task_frag.page = NULL;
tsk->wake_q.next = NULL;
- account_kernel_stack(ti, 1);
+ account_kernel_stack(stack, 1);
return tsk;
-free_ti:
- free_thread_info(ti);
+free_stack:
+ free_thread_stack(stack);
free_tsk:
free_task_struct(tsk);
return NULL;
@@ -832,8 +834,7 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
mm = get_task_mm(task);
if (mm && mm != current->mm &&
- !ptrace_may_access(task, mode) &&
- !capable(CAP_SYS_RESOURCE)) {
+ !ptrace_may_access(task, mode)) {
mmput(mm);
mm = ERR_PTR(-EACCES);
}
diff --git a/kernel/kthread.c b/kernel/kthread.c
index 698b8dec3074..d9b0be5c6a5f 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -65,7 +65,7 @@ static inline struct kthread *to_kthread(struct task_struct *k)
static struct kthread *to_live_kthread(struct task_struct *k)
{
struct completion *vfork = ACCESS_ONCE(k->vfork_done);
- if (likely(vfork))
+ if (likely(vfork) && try_get_task_stack(k))
return __to_kthread(vfork);
return NULL;
}
@@ -427,8 +427,10 @@ void kthread_unpark(struct task_struct *k)
{
struct kthread *kthread = to_live_kthread(k);
- if (kthread)
+ if (kthread) {
__kthread_unpark(k, kthread);
+ put_task_stack(k);
+ }
}
EXPORT_SYMBOL_GPL(kthread_unpark);
@@ -457,6 +459,7 @@ int kthread_park(struct task_struct *k)
wait_for_completion(&kthread->parked);
}
}
+ put_task_stack(k);
ret = 0;
}
return ret;
@@ -492,6 +495,7 @@ int kthread_stop(struct task_struct *k)
__kthread_unpark(k, kthread);
wake_up_process(k);
wait_for_completion(&kthread->exited);
+ put_task_stack(k);
}
ret = k->exit_code;
put_task_struct(k);
diff --git a/kernel/locking/mutex-debug.c b/kernel/locking/mutex-debug.c
index 3ef3736002d8..9c951fade415 100644
--- a/kernel/locking/mutex-debug.c
+++ b/kernel/locking/mutex-debug.c
@@ -49,21 +49,21 @@ void debug_mutex_free_waiter(struct mutex_waiter *waiter)
}
void debug_mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter,
- struct thread_info *ti)
+ struct task_struct *task)
{
SMP_DEBUG_LOCKS_WARN_ON(!spin_is_locked(&lock->wait_lock));
/* Mark the current thread as blocked on the lock: */
- ti->task->blocked_on = waiter;
+ task->blocked_on = waiter;
}
void mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter,
- struct thread_info *ti)
+ struct task_struct *task)
{
DEBUG_LOCKS_WARN_ON(list_empty(&waiter->list));
- DEBUG_LOCKS_WARN_ON(waiter->task != ti->task);
- DEBUG_LOCKS_WARN_ON(ti->task->blocked_on != waiter);
- ti->task->blocked_on = NULL;
+ DEBUG_LOCKS_WARN_ON(waiter->task != task);
+ DEBUG_LOCKS_WARN_ON(task->blocked_on != waiter);
+ task->blocked_on = NULL;
list_del_init(&waiter->list);
waiter->task = NULL;
diff --git a/kernel/locking/mutex-debug.h b/kernel/locking/mutex-debug.h
index 0799fd3e4cfa..d06ae3bb46c5 100644
--- a/kernel/locking/mutex-debug.h
+++ b/kernel/locking/mutex-debug.h
@@ -20,9 +20,9 @@ extern void debug_mutex_wake_waiter(struct mutex *lock,
extern void debug_mutex_free_waiter(struct mutex_waiter *waiter);
extern void debug_mutex_add_waiter(struct mutex *lock,
struct mutex_waiter *waiter,
- struct thread_info *ti);
+ struct task_struct *task);
extern void mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter,
- struct thread_info *ti);
+ struct task_struct *task);
extern void debug_mutex_unlock(struct mutex *lock);
extern void debug_mutex_init(struct mutex *lock, const char *name,
struct lock_class_key *key);
diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c
index 14b9cca36b05..bf5277ee11d3 100644
--- a/kernel/locking/mutex.c
+++ b/kernel/locking/mutex.c
@@ -549,7 +549,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
goto skip_wait;
debug_mutex_lock_common(lock, &waiter);
- debug_mutex_add_waiter(lock, &waiter, task_thread_info(task));
+ debug_mutex_add_waiter(lock, &waiter, task);
/* add waiting tasks to the end of the waitqueue (FIFO): */
list_add_tail(&waiter.list, &lock->wait_list);
@@ -596,7 +596,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
}
__set_task_state(task, TASK_RUNNING);
- mutex_remove_waiter(lock, &waiter, current_thread_info());
+ mutex_remove_waiter(lock, &waiter, task);
/* set it to 0 if there are no waiters left: */
if (likely(list_empty(&lock->wait_list)))
atomic_set(&lock->count, 0);
@@ -617,7 +617,7 @@ skip_wait:
return 0;
err:
- mutex_remove_waiter(lock, &waiter, task_thread_info(task));
+ mutex_remove_waiter(lock, &waiter, task);
spin_unlock_mutex(&lock->wait_lock, flags);
debug_mutex_free_waiter(&waiter);
mutex_release(&lock->dep_map, 1, ip);
diff --git a/kernel/locking/mutex.h b/kernel/locking/mutex.h
index 5cda397607f2..a68bae5e852a 100644
--- a/kernel/locking/mutex.h
+++ b/kernel/locking/mutex.h
@@ -13,7 +13,7 @@
do { spin_lock(lock); (void)(flags); } while (0)
#define spin_unlock_mutex(lock, flags) \
do { spin_unlock(lock); (void)(flags); } while (0)
-#define mutex_remove_waiter(lock, waiter, ti) \
+#define mutex_remove_waiter(lock, waiter, task) \
__list_del((waiter)->list.prev, (waiter)->list.next)
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index a4d4de05b2d1..75c950ede9c7 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -511,6 +511,41 @@ struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
unsigned long flags;
/*
+ * If a spinner is present, there is a chance that the load of
+ * rwsem_has_spinner() in rwsem_wake() can be reordered with
+ * respect to decrement of rwsem count in __up_write() leading
+ * to wakeup being missed.
+ *
+ * spinning writer up_write caller
+ * --------------- -----------------------
+ * [S] osq_unlock() [L] osq
+ * spin_lock(wait_lock)
+ * sem->count=0xFFFFFFFF00000001
+ * +0xFFFFFFFF00000000
+ * count=sem->count
+ * MB
+ * sem->count=0xFFFFFFFE00000001
+ * -0xFFFFFFFF00000001
+ * RMB
+ * spin_trylock(wait_lock)
+ * return
+ * rwsem_try_write_lock(count)
+ * spin_unlock(wait_lock)
+ * schedule()
+ *
+ * Reordering of atomic_long_sub_return_release() in __up_write()
+ * and rwsem_has_spinner() in rwsem_wake() can cause missing of
+ * wakeup in up_write() context. In spinning writer, sem->count
+ * and local variable count is 0XFFFFFFFE00000001. It would result
+ * in rwsem_try_write_lock() failing to acquire rwsem and spinning
+ * writer going to sleep in rwsem_down_write_failed().
+ *
+ * The smp_rmb() here is to make sure that the spinner state is
+ * consulted after sem->count is updated in up_write context.
+ */
+ smp_rmb();
+
+ /*
* If a spinner is present, it is not necessary to do the wakeup.
* Try to do wakeup only if the trylock succeeds to minimize
* spinlock contention which may introduce too much delay in the
diff --git a/kernel/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/printk/printk.c b/kernel/printk/printk.c
index 9fcb521fab0e..dca87791e9c1 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -3180,9 +3180,8 @@ void show_regs_print_info(const char *log_lvl)
{
dump_stack_print_info(log_lvl);
- printk("%stask: %p ti: %p task.ti: %p\n",
- log_lvl, current, current_thread_info(),
- task_thread_info(current));
+ printk("%stask: %p task.stack: %p\n",
+ log_lvl, current, task_stack_page(current));
}
#endif
diff --git a/kernel/resource.c b/kernel/resource.c
index 4c9835c09dcd..c09d484f7b5f 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -105,16 +105,25 @@ static int r_show(struct seq_file *m, void *v)
{
struct resource *root = m->private;
struct resource *r = v, *p;
+ unsigned long long start, end;
int width = root->end < 0x10000 ? 4 : 8;
int depth;
for (depth = 0, p = r; depth < MAX_IORES_LEVEL; depth++, p = p->parent)
if (p->parent == root)
break;
+
+ if (file_ns_capable(m->file, &init_user_ns, CAP_SYS_ADMIN)) {
+ start = r->start;
+ end = r->end;
+ } else {
+ start = end = 0;
+ }
+
seq_printf(m, "%*s%0*llx-%0*llx : %s\n",
depth * 2, "",
- width, (unsigned long long) r->start,
- width, (unsigned long long) r->end,
+ width, start,
+ width, end,
r->name ? r->name : "<BAD>");
return 0;
}
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 0071785e698b..4ecca604e64b 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -6924,6 +6924,9 @@ enum s_alloc {
* Build an iteration mask that can exclude certain CPUs from the upwards
* domain traversal.
*
+ * Only CPUs that can arrive at this group should be considered to continue
+ * balancing.
+ *
* Asymmetric node setups can result in situations where the domain tree is of
* unequal depth, make sure to skip domains that already cover the entire
* range.
@@ -6935,18 +6938,31 @@ enum s_alloc {
*/
static void build_group_mask(struct sched_domain *sd, struct sched_group *sg)
{
- const struct cpumask *span = sched_domain_span(sd);
+ const struct cpumask *sg_span = sched_group_cpus(sg);
struct sd_data *sdd = sd->private;
struct sched_domain *sibling;
int i;
- for_each_cpu(i, span) {
+ for_each_cpu(i, sg_span) {
sibling = *per_cpu_ptr(sdd->sd, i);
- if (!cpumask_test_cpu(i, sched_domain_span(sibling)))
+
+ /*
+ * Can happen in the asymmetric case, where these siblings are
+ * unused. The mask will not be empty because those CPUs that
+ * do have the top domain _should_ span the domain.
+ */
+ if (!sibling->child)
+ continue;
+
+ /* If we would not end up here, we can't continue from here */
+ if (!cpumask_equal(sg_span, sched_domain_span(sibling->child)))
continue;
cpumask_set_cpu(i, sched_group_mask(sg));
}
+
+ /* We must not have empty masks here */
+ WARN_ON_ONCE(cpumask_empty(sched_group_mask(sg)));
}
/*
@@ -9216,11 +9232,20 @@ cpu_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
if (IS_ERR(tg))
return ERR_PTR(-ENOMEM);
- sched_online_group(tg, parent);
-
return &tg->css;
}
+/* Expose task group only after completing cgroup initialization */
+static int cpu_cgroup_css_online(struct cgroup_subsys_state *css)
+{
+ struct task_group *tg = css_tg(css);
+ struct task_group *parent = css_tg(css->parent);
+
+ if (parent)
+ sched_online_group(tg, parent);
+ return 0;
+}
+
static void cpu_cgroup_css_released(struct cgroup_subsys_state *css)
{
struct task_group *tg = css_tg(css);
@@ -9602,6 +9627,7 @@ static struct cftype cpu_files[] = {
struct cgroup_subsys cpu_cgrp_subsys = {
.css_alloc = cpu_cgroup_css_alloc,
+ .css_online = cpu_cgroup_css_online,
.css_released = cpu_cgroup_css_released,
.css_free = cpu_cgroup_css_free,
.fork = cpu_cgroup_fork,
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 75bfbb336722..e12309c1b07b 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -47,6 +47,7 @@ struct sugov_policy {
s64 up_rate_delay_ns;
s64 down_rate_delay_ns;
unsigned int next_freq;
+ unsigned int cached_raw_freq;
/* The next fields are only needed if fast switch cannot be used. */
struct irq_work irq_work;
@@ -63,7 +64,6 @@ struct sugov_cpu {
struct update_util_data update_util;
struct sugov_policy *sg_policy;
- unsigned int cached_raw_freq;
unsigned long iowait_boost;
unsigned long iowait_boost_max;
u64 last_update;
@@ -72,6 +72,11 @@ struct sugov_cpu {
unsigned long util;
unsigned long max;
unsigned int flags;
+
+ /* The field below is for single-CPU policies only. */
+#ifdef CONFIG_NO_HZ_COMMON
+ unsigned long saved_idle_calls;
+#endif
};
static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu);
@@ -127,22 +132,20 @@ static void sugov_update_commit(struct sugov_policy *sg_policy, u64 time,
if (sugov_up_down_rate_limit(sg_policy, time, next_freq))
return;
+ if (sg_policy->next_freq == next_freq)
+ return;
+
+ sg_policy->next_freq = next_freq;
+ sg_policy->last_freq_update_time = time;
+
if (policy->fast_switch_enabled) {
- if (sg_policy->next_freq == next_freq) {
- trace_cpu_frequency(policy->cur, smp_processor_id());
- return;
- }
- sg_policy->next_freq = next_freq;
- sg_policy->last_freq_update_time = time;
next_freq = cpufreq_driver_fast_switch(policy, next_freq);
if (next_freq == CPUFREQ_ENTRY_INVALID)
return;
policy->cur = next_freq;
trace_cpu_frequency(next_freq, smp_processor_id());
- } else if (sg_policy->next_freq != next_freq) {
- sg_policy->next_freq = next_freq;
- sg_policy->last_freq_update_time = time;
+ } else {
sg_policy->work_in_progress = true;
irq_work_queue(&sg_policy->irq_work);
}
@@ -150,7 +153,7 @@ static void sugov_update_commit(struct sugov_policy *sg_policy, u64 time,
/**
* get_next_freq - Compute a new frequency for a given cpufreq policy.
- * @sg_cpu: schedutil cpu object to compute the new frequency for.
+ * @sg_policy: schedutil policy object to compute the new frequency for.
* @util: Current CPU utilization.
* @max: CPU capacity.
*
@@ -170,19 +173,18 @@ static void sugov_update_commit(struct sugov_policy *sg_policy, u64 time,
* next_freq (as calculated above) is returned, subject to policy min/max and
* cpufreq driver limitations.
*/
-static unsigned int get_next_freq(struct sugov_cpu *sg_cpu, unsigned long util,
- unsigned long max)
+static unsigned int get_next_freq(struct sugov_policy *sg_policy,
+ unsigned long util, unsigned long max)
{
- struct sugov_policy *sg_policy = sg_cpu->sg_policy;
struct cpufreq_policy *policy = sg_policy->policy;
unsigned int freq = arch_scale_freq_invariant() ?
policy->cpuinfo.max_freq : policy->cur;
freq = (freq + (freq >> 2)) * util / max;
- if (freq == sg_cpu->cached_raw_freq && sg_policy->next_freq != UINT_MAX)
+ if (freq == sg_policy->cached_raw_freq && sg_policy->next_freq != UINT_MAX)
return sg_policy->next_freq;
- sg_cpu->cached_raw_freq = freq;
+ sg_policy->cached_raw_freq = freq;
return cpufreq_driver_resolve_freq(policy, freq);
}
@@ -248,6 +250,19 @@ static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, unsigned long *util,
sg_cpu->iowait_boost >>= 1;
}
+#ifdef CONFIG_NO_HZ_COMMON
+static bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu)
+{
+ unsigned long idle_calls = tick_nohz_get_idle_calls();
+ bool ret = idle_calls == sg_cpu->saved_idle_calls;
+
+ sg_cpu->saved_idle_calls = idle_calls;
+ return ret;
+}
+#else
+static inline bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu) { return false; }
+#endif /* CONFIG_NO_HZ_COMMON */
+
static void sugov_update_single(struct update_util_data *hook, u64 time,
unsigned int flags)
{
@@ -256,6 +271,7 @@ static void sugov_update_single(struct update_util_data *hook, u64 time,
struct cpufreq_policy *policy = sg_policy->policy;
unsigned long util, max;
unsigned int next_f;
+ bool busy;
sugov_set_iowait_boost(sg_cpu, time, flags);
sg_cpu->last_update = time;
@@ -263,40 +279,37 @@ static void sugov_update_single(struct update_util_data *hook, u64 time,
if (!sugov_should_update_freq(sg_policy, time))
return;
+ busy = sugov_cpu_is_busy(sg_cpu);
+
if (flags & SCHED_CPUFREQ_DL) {
next_f = policy->cpuinfo.max_freq;
} else {
sugov_get_util(&util, &max, time);
sugov_iowait_boost(sg_cpu, &util, &max);
- next_f = get_next_freq(sg_cpu, util, max);
+ next_f = get_next_freq(sg_policy, util, max);
+ /*
+ * Do not reduce the frequency if the CPU has not been idle
+ * recently, as the reduction is likely to be premature then.
+ */
+ if (busy && next_f < sg_policy->next_freq)
+ next_f = sg_policy->next_freq;
}
sugov_update_commit(sg_policy, time, next_f);
}
-static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu,
- unsigned long util, unsigned long max,
- unsigned int flags)
+static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu)
{
struct sugov_policy *sg_policy = sg_cpu->sg_policy;
struct cpufreq_policy *policy = sg_policy->policy;
- unsigned int max_f = policy->cpuinfo.max_freq;
u64 last_freq_update_time = sg_policy->last_freq_update_time;
+ unsigned long util = 0, max = 1;
unsigned int j;
- if (flags & SCHED_CPUFREQ_DL)
- return max_f;
-
- sugov_iowait_boost(sg_cpu, &util, &max);
-
for_each_cpu(j, policy->cpus) {
- struct sugov_cpu *j_sg_cpu;
+ struct sugov_cpu *j_sg_cpu = &per_cpu(sugov_cpu, j);
unsigned long j_util, j_max;
s64 delta_ns;
- if (j == smp_processor_id())
- continue;
-
- j_sg_cpu = &per_cpu(sugov_cpu, j);
/*
* If the CPU utilization was last updated before the previous
* frequency update and the time elapsed between the last update
@@ -310,7 +323,7 @@ static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu,
continue;
}
if (j_sg_cpu->flags & SCHED_CPUFREQ_DL)
- return max_f;
+ return policy->cpuinfo.max_freq;
j_util = j_sg_cpu->util;
j_max = j_sg_cpu->max;
@@ -322,7 +335,7 @@ static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu,
sugov_iowait_boost(j_sg_cpu, &util, &max);
}
- return get_next_freq(sg_cpu, util, max);
+ return get_next_freq(sg_policy, util, max);
}
static void sugov_update_shared(struct update_util_data *hook, u64 time,
@@ -345,7 +358,11 @@ static void sugov_update_shared(struct update_util_data *hook, u64 time,
sg_cpu->last_update = time;
if (sugov_should_update_freq(sg_policy, time)) {
- next_f = sugov_next_freq_shared(sg_cpu, util, max, flags);
+ if (flags & SCHED_CPUFREQ_DL)
+ next_f = sg_policy->policy->cpuinfo.max_freq;
+ else
+ next_f = sugov_next_freq_shared(sg_cpu);
+
sugov_update_commit(sg_policy, time, next_f);
}
@@ -371,15 +388,15 @@ static void sugov_irq_work(struct irq_work *irq_work)
sg_policy = container_of(irq_work, struct sugov_policy, irq_work);
/*
- * For Real Time and Deadline tasks, schedutil governor shoots the
- * frequency to maximum. And special care must be taken to ensure that
- * this kthread doesn't result in that.
+ * For RT and deadline tasks, the schedutil governor shoots the
+ * frequency to maximum. Special care must be taken to ensure that this
+ * kthread doesn't result in the same behavior.
*
* This is (mostly) guaranteed by the work_in_progress flag. The flag is
- * updated only at the end of the sugov_work() and before that schedutil
- * rejects all other frequency scaling requests.
+ * updated only at the end of the sugov_work() function and before that
+ * the schedutil governor rejects all other frequency scaling requests.
*
- * Though there is a very rare case where the RT thread yields right
+ * There is a very rare case though, where the RT thread yields right
* after the work_in_progress flag is cleared. The effects of that are
* neglected for now.
*/
@@ -489,15 +506,12 @@ static struct sugov_policy *sugov_policy_alloc(struct cpufreq_policy *policy)
return NULL;
sg_policy->policy = policy;
- init_irq_work(&sg_policy->irq_work, sugov_irq_work);
- mutex_init(&sg_policy->work_lock);
raw_spin_lock_init(&sg_policy->update_lock);
return sg_policy;
}
static void sugov_policy_free(struct sugov_policy *sg_policy)
{
- mutex_destroy(&sg_policy->work_lock);
kfree(sg_policy);
}
@@ -531,6 +545,9 @@ static int sugov_kthread_create(struct sugov_policy *sg_policy)
sg_policy->thread = thread;
kthread_bind_mask(thread, policy->related_cpus);
+ init_irq_work(&sg_policy->irq_work, sugov_irq_work);
+ mutex_init(&sg_policy->work_lock);
+
wake_up_process(thread);
return 0;
@@ -544,6 +561,7 @@ static void sugov_kthread_stop(struct sugov_policy *sg_policy)
flush_kthread_worker(&sg_policy->worker);
kthread_stop(sg_policy->thread);
+ mutex_destroy(&sg_policy->work_lock);
}
static struct sugov_tunables *sugov_tunables_alloc(struct sugov_policy *sg_policy)
@@ -578,9 +596,13 @@ static int sugov_init(struct cpufreq_policy *policy)
if (policy->governor_data)
return -EBUSY;
+ cpufreq_enable_fast_switch(policy);
+
sg_policy = sugov_policy_alloc(policy);
- if (!sg_policy)
- return -ENOMEM;
+ if (!sg_policy) {
+ ret = -ENOMEM;
+ goto disable_fast_switch;
+ }
ret = sugov_kthread_create(sg_policy);
if (ret)
@@ -623,13 +645,11 @@ static int sugov_init(struct cpufreq_policy *policy)
if (ret)
goto fail;
- out:
+out:
mutex_unlock(&global_tunables_lock);
-
- cpufreq_enable_fast_switch(policy);
return 0;
- fail:
+fail:
policy->governor_data = NULL;
sugov_tunables_free(tunables);
@@ -640,6 +660,10 @@ free_sg_policy:
mutex_unlock(&global_tunables_lock);
sugov_policy_free(sg_policy);
+
+disable_fast_switch:
+ cpufreq_disable_fast_switch(policy);
+
pr_err("initialization failed (error %d)\n", ret);
return ret;
}
@@ -650,8 +674,6 @@ static int sugov_exit(struct cpufreq_policy *policy)
struct sugov_tunables *tunables = sg_policy->tunables;
unsigned int count;
- cpufreq_disable_fast_switch(policy);
-
mutex_lock(&global_tunables_lock);
count = gov_attr_set_put(&tunables->attr_set, &sg_policy->tunables_hook);
@@ -664,6 +686,7 @@ static int sugov_exit(struct cpufreq_policy *policy)
sugov_kthread_stop(sg_policy);
sugov_policy_free(sg_policy);
+ cpufreq_disable_fast_switch(policy);
return 0;
}
@@ -681,25 +704,19 @@ static int sugov_start(struct cpufreq_policy *policy)
sg_policy->next_freq = UINT_MAX;
sg_policy->work_in_progress = false;
sg_policy->need_freq_update = false;
+ sg_policy->cached_raw_freq = 0;
for_each_cpu(cpu, policy->cpus) {
struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu);
+ memset(sg_cpu, 0, sizeof(*sg_cpu));
sg_cpu->sg_policy = sg_policy;
- if (policy_is_shared(policy)) {
- sg_cpu->util = 0;
- sg_cpu->max = 0;
- sg_cpu->flags = SCHED_CPUFREQ_DL;
- sg_cpu->last_update = 0;
- sg_cpu->cached_raw_freq = 0;
- sg_cpu->iowait_boost = 0;
- sg_cpu->iowait_boost_max = policy->cpuinfo.max_freq;
- cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util,
- sugov_update_shared);
- } else {
- cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util,
- sugov_update_single);
- }
+ sg_cpu->flags = SCHED_CPUFREQ_DL;
+ sg_cpu->iowait_boost_max = policy->cpuinfo.max_freq;
+ cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util,
+ policy_is_shared(policy) ?
+ sugov_update_shared :
+ sugov_update_single);
}
return 0;
}
@@ -714,9 +731,10 @@ static int sugov_stop(struct cpufreq_policy *policy)
synchronize_sched();
- irq_work_sync(&sg_policy->irq_work);
- kthread_cancel_work_sync(&sg_policy->work);
-
+ if (!policy->fast_switch_enabled) {
+ irq_work_sync(&sg_policy->irq_work);
+ kthread_cancel_work_sync(&sg_policy->work);
+ }
return 0;
}
diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c
index 1d00cf8c00fa..14225d5d8617 100644
--- a/kernel/sched/cpupri.c
+++ b/kernel/sched/cpupri.c
@@ -279,3 +279,14 @@ void cpupri_cleanup(struct cpupri *cp)
for (i = 0; i < CPUPRI_NR_PRIORITIES; i++)
free_cpumask_var(cp->pri_to_cpu[i].mask);
}
+
+/*
+ * cpupri_check_rt - check if CPU has a RT task
+ * should be called from rcu-sched read section.
+ */
+bool cpupri_check_rt(void)
+{
+ int cpu = raw_smp_processor_id();
+
+ return cpu_rq(cpu)->rd->cpupri.cpu_to_pri[cpu] > CPUPRI_NORMAL;
+}
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 422438d43d90..853064319b0d 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -35,6 +35,8 @@
#include "sched.h"
#include <trace/events/sched.h>
#include "tune.h"
+#include "walt.h"
+
/*
* Targeted preemption latency for CPU-bound tasks:
* (default: 6ms * (1 + ilog(ncpus)), units: nanoseconds)
@@ -6548,9 +6550,11 @@ static int find_new_capacity(struct energy_env *eenv,
return idx;
}
-static int group_idle_state(struct sched_group *sg)
+static int group_idle_state(struct energy_env *eenv, struct sched_group *sg)
{
int i, state = INT_MAX;
+ int src_in_grp, dst_in_grp;
+ long grp_util = 0;
/* Find the shallowest idle state in the sched group. */
for_each_cpu(i, sched_group_cpus(sg))
@@ -6559,6 +6563,54 @@ static int group_idle_state(struct sched_group *sg)
/* Take non-cpuidle idling into account (active idle/arch_cpu_idle()) */
state++;
+ /*
+ * Try to estimate if a deeper idle state is
+ * achievable when we move the task.
+ */
+ for_each_cpu(i, sched_group_cpus(sg))
+ grp_util += cpu_util(i);
+
+ src_in_grp = cpumask_test_cpu(eenv->src_cpu, sched_group_cpus(sg));
+ dst_in_grp = cpumask_test_cpu(eenv->dst_cpu, sched_group_cpus(sg));
+ if (src_in_grp == dst_in_grp) {
+ /* both CPUs under consideration are in the same group or not in
+ * either group, migration should leave idle state the same.
+ */
+ goto end;
+ }
+ /* add or remove util as appropriate to indicate what group util
+ * will be (worst case - no concurrent execution) after moving the task
+ */
+ grp_util += src_in_grp ? -eenv->util_delta : eenv->util_delta;
+
+ if (grp_util <=
+ ((long)sg->sgc->max_capacity * (int)sg->group_weight)) {
+ /* after moving, this group is at most partly
+ * occupied, so it should have some idle time.
+ */
+ int max_idle_state_idx = sg->sge->nr_idle_states - 2;
+ int new_state = grp_util * max_idle_state_idx;
+ if (grp_util <= 0)
+ /* group will have no util, use lowest state */
+ new_state = max_idle_state_idx + 1;
+ else {
+ /* for partially idle, linearly map util to idle
+ * states, excluding the lowest one. This does not
+ * correspond to the state we expect to enter in
+ * reality, but an indication of what might happen.
+ */
+ new_state = min(max_idle_state_idx, (int)
+ (new_state / sg->sgc->max_capacity));
+ new_state = max_idle_state_idx - new_state;
+ }
+ state = new_state;
+ } else {
+ /* After moving, the group will be fully occupied
+ * so assume it will not be idle at all.
+ */
+ state = 0;
+ }
+end:
return state;
}
@@ -6631,8 +6683,9 @@ static int sched_group_energy(struct energy_env *eenv)
}
}
- idle_idx = group_idle_state(sg);
+ idle_idx = group_idle_state(eenv, sg);
group_util = group_norm_util(eenv, sg);
+
sg_busy_energy = (group_util * sg->sge->cap_states[cap_idx].power)
>> SCHED_CAPACITY_SHIFT;
sg_idle_energy = ((SCHED_LOAD_SCALE-group_util)
@@ -7337,48 +7390,59 @@ static int start_cpu(bool boosted)
return boosted ? rd->max_cap_orig_cpu : rd->min_cap_orig_cpu;
}
-static inline int find_best_target(struct task_struct *p, bool boosted, bool prefer_idle)
+static inline int find_best_target(struct task_struct *p, int *backup_cpu,
+ bool boosted, bool prefer_idle)
{
- int target_cpu = -1;
- unsigned long target_util = prefer_idle ? ULONG_MAX : 0;
- unsigned long backup_capacity = ULONG_MAX;
- int best_idle_cpu = -1;
- int best_idle_cstate = INT_MAX;
- int backup_cpu = -1;
+ unsigned long best_idle_min_cap_orig = ULONG_MAX;
unsigned long min_util = boosted_task_util(p);
+ unsigned long target_capacity = ULONG_MAX;
+ unsigned long min_wake_util = ULONG_MAX;
+ unsigned long target_max_spare_cap = 0;
+ unsigned long target_util = ULONG_MAX;
+ unsigned long best_active_util = ULONG_MAX;
+ int best_idle_cstate = INT_MAX;
struct sched_domain *sd;
struct sched_group *sg;
- int cpu = start_cpu(boosted);
+ int best_active_cpu = -1;
+ int best_idle_cpu = -1;
+ int target_cpu = -1;
+ int cpu, i;
+
+ *backup_cpu = -1;
schedstat_inc(p, se.statistics.nr_wakeups_fbt_attempts);
schedstat_inc(this_rq(), eas_stats.fbt_attempts);
+ /* Find start CPU based on boost value */
+ cpu = start_cpu(boosted);
if (cpu < 0) {
schedstat_inc(p, se.statistics.nr_wakeups_fbt_no_cpu);
schedstat_inc(this_rq(), eas_stats.fbt_no_cpu);
- return target_cpu;
+ return -1;
}
+ /* Find SD for the start CPU */
sd = rcu_dereference(per_cpu(sd_ea, cpu));
-
if (!sd) {
schedstat_inc(p, se.statistics.nr_wakeups_fbt_no_sd);
schedstat_inc(this_rq(), eas_stats.fbt_no_sd);
- return target_cpu;
+ return -1;
}
+ /* Scan CPUs in all SDs */
sg = sd->groups;
-
do {
- int i;
-
for_each_cpu_and(i, tsk_cpus_allowed(p), sched_group_cpus(sg)) {
- unsigned long cur_capacity, new_util, wake_util;
- unsigned long min_wake_util = ULONG_MAX;
+ unsigned long capacity_curr = capacity_curr_of(i);
+ unsigned long capacity_orig = capacity_orig_of(i);
+ unsigned long wake_util, new_util;
if (!cpu_online(i))
continue;
+ if (walt_cpu_high_irqload(i))
+ continue;
+
/*
* p's blocked utilization is still accounted for on prev_cpu
* so prev_cpu will receive a negative bias due to the double
@@ -7393,65 +7457,204 @@ static inline int find_best_target(struct task_struct *p, bool boosted, bool pre
* than the one required to boost the task.
*/
new_util = max(min_util, new_util);
-
- if (new_util > capacity_orig_of(i))
+ if (new_util > capacity_orig)
continue;
/*
- * Unconditionally favoring tasks that prefer idle cpus to
+ * Case A) Latency sensitive tasks
+ *
+ * Unconditionally favoring tasks that prefer idle CPU to
* improve latency.
+ *
+ * Looking for:
+ * - an idle CPU, whatever its idle_state is, since
+ * the first CPUs we explore are more likely to be
+ * reserved for latency sensitive tasks.
+ * - a non idle CPU where the task fits in its current
+ * capacity and has the maximum spare capacity.
+ * - a non idle CPU with lower contention from other
+ * tasks and running at the lowest possible OPP.
+ *
+ * The last two goals tries to favor a non idle CPU
+ * where the task can run as if it is "almost alone".
+ * A maximum spare capacity CPU is favoured since
+ * the task already fits into that CPU's capacity
+ * without waiting for an OPP chance.
+ *
+ * The following code path is the only one in the CPUs
+ * exploration loop which is always used by
+ * prefer_idle tasks. It exits the loop with wither a
+ * best_active_cpu or a target_cpu which should
+ * represent an optimal choice for latency sensitive
+ * tasks.
*/
- if (idle_cpu(i) && prefer_idle) {
- schedstat_inc(p, se.statistics.nr_wakeups_fbt_pref_idle);
- schedstat_inc(this_rq(), eas_stats.fbt_pref_idle);
- return i;
- }
+ if (prefer_idle) {
- cur_capacity = capacity_curr_of(i);
-
- if (new_util < cur_capacity) {
- if (cpu_rq(i)->nr_running) {
- /*
- * Find a target cpu with the lowest/highest
- * utilization if prefer_idle/!prefer_idle.
- */
- if (prefer_idle) {
- /* Favor the CPU that last ran the task */
- if (new_util > target_util ||
- wake_util > min_wake_util)
- continue;
- min_wake_util = wake_util;
- target_util = new_util;
- target_cpu = i;
- } else if (target_util < new_util) {
- target_util = new_util;
- target_cpu = i;
- }
- } else if (!prefer_idle) {
- int idle_idx = idle_get_state_idx(cpu_rq(i));
+ /*
+ * Case A.1: IDLE CPU
+ * Return the first IDLE CPU we find.
+ */
+ if (idle_cpu(i)) {
+ schedstat_inc(p, se.statistics.nr_wakeups_fbt_pref_idle);
+ schedstat_inc(this_rq(), eas_stats.fbt_pref_idle);
- if (best_idle_cpu < 0 ||
- (sysctl_sched_cstate_aware &&
- best_idle_cstate > idle_idx)) {
- best_idle_cstate = idle_idx;
- best_idle_cpu = i;
- }
+ trace_sched_find_best_target(p,
+ prefer_idle, min_util,
+ cpu, best_idle_cpu,
+ best_active_cpu, i);
+
+ return i;
}
- } else if (backup_capacity > cur_capacity) {
- /* Find a backup cpu with least capacity. */
- backup_capacity = cur_capacity;
- backup_cpu = i;
+
+ /*
+ * Case A.2: Target ACTIVE CPU
+ * Favor CPUs with max spare capacity.
+ */
+ if ((capacity_curr > new_util) &&
+ (capacity_orig - new_util > target_max_spare_cap)) {
+ target_max_spare_cap = capacity_orig - new_util;
+ target_cpu = i;
+ continue;
+ }
+ if (target_cpu != -1)
+ continue;
+
+
+ /*
+ * Case A.3: Backup ACTIVE CPU
+ * Favor CPUs with:
+ * - lower utilization due to other tasks
+ * - lower utilization with the task in
+ */
+ if (wake_util > min_wake_util)
+ continue;
+ if (new_util > best_active_util)
+ continue;
+ min_wake_util = wake_util;
+ best_active_util = new_util;
+ best_active_cpu = i;
+ continue;
}
+
+ /*
+ * Case B) Non latency sensitive tasks on IDLE CPUs.
+ *
+ * Find an optimal backup IDLE CPU for non latency
+ * sensitive tasks.
+ *
+ * Looking for:
+ * - minimizing the capacity_orig,
+ * i.e. preferring LITTLE CPUs
+ * - favoring shallowest idle states
+ * i.e. avoid to wakeup deep-idle CPUs
+ *
+ * The following code path is used by non latency
+ * sensitive tasks if IDLE CPUs are available. If at
+ * least one of such CPUs are available it sets the
+ * best_idle_cpu to the most suitable idle CPU to be
+ * selected.
+ *
+ * If idle CPUs are available, favour these CPUs to
+ * improve performances by spreading tasks.
+ * Indeed, the energy_diff() computed by the caller
+ * will take care to ensure the minimization of energy
+ * consumptions without affecting performance.
+ */
+ if (idle_cpu(i)) {
+ int idle_idx = idle_get_state_idx(cpu_rq(i));
+
+ /* Select idle CPU with lower cap_orig */
+ if (capacity_orig > best_idle_min_cap_orig)
+ continue;
+
+ /*
+ * Skip CPUs in deeper idle state, but only
+ * if they are also less energy efficient.
+ * IOW, prefer a deep IDLE LITTLE CPU vs a
+ * shallow idle big CPU.
+ */
+ if (sysctl_sched_cstate_aware &&
+ best_idle_cstate <= idle_idx)
+ continue;
+
+ /* Keep track of best idle CPU */
+ best_idle_min_cap_orig = capacity_orig;
+ best_idle_cstate = idle_idx;
+ best_idle_cpu = i;
+ continue;
+ }
+
+ /*
+ * Case C) Non latency sensitive tasks on ACTIVE CPUs.
+ *
+ * Pack tasks in the most energy efficient capacities.
+ *
+ * This task packing strategy prefers more energy
+ * efficient CPUs (i.e. pack on smaller maximum
+ * capacity CPUs) while also trying to spread tasks to
+ * run them all at the lower OPP.
+ *
+ * This assumes for example that it's more energy
+ * efficient to run two tasks on two CPUs at a lower
+ * OPP than packing both on a single CPU but running
+ * that CPU at an higher OPP.
+ *
+ * Thus, this case keep track of the CPU with the
+ * smallest maximum capacity and highest spare maximum
+ * capacity.
+ */
+
+ /* Favor CPUs with smaller capacity */
+ if (capacity_orig > target_capacity)
+ continue;
+
+ /* Favor CPUs with maximum spare capacity */
+ if ((capacity_orig - new_util) < target_max_spare_cap)
+ continue;
+
+ target_max_spare_cap = capacity_orig - new_util;
+ target_capacity = capacity_orig;
+ target_util = new_util;
+ target_cpu = i;
}
+
} while (sg = sg->next, sg != sd->groups);
- if (target_cpu < 0)
- target_cpu = best_idle_cpu >= 0 ? best_idle_cpu : backup_cpu;
+ /*
+ * For non latency sensitive tasks, cases B and C in the previous loop,
+ * we pick the best IDLE CPU only if we was not able to find a target
+ * ACTIVE CPU.
+ *
+ * Policies priorities:
+ *
+ * - prefer_idle tasks:
+ *
+ * a) IDLE CPU available, we return immediately
+ * b) ACTIVE CPU where task fits and has the bigger maximum spare
+ * capacity (i.e. target_cpu)
+ * c) ACTIVE CPU with less contention due to other tasks
+ * (i.e. best_active_cpu)
+ *
+ * - NON prefer_idle tasks:
+ *
+ * a) ACTIVE CPU: target_cpu
+ * b) IDLE CPU: best_idle_cpu
+ */
+ if (target_cpu == -1)
+ target_cpu = prefer_idle
+ ? best_active_cpu
+ : best_idle_cpu;
+ else
+ *backup_cpu = prefer_idle
+ ? best_active_cpu
+ : best_idle_cpu;
- if (target_cpu >= 0) {
- schedstat_inc(p, se.statistics.nr_wakeups_fbt_count);
- schedstat_inc(this_rq(), eas_stats.fbt_count);
- }
+ trace_sched_find_best_target(p, prefer_idle, min_util, cpu,
+ best_idle_cpu, best_active_cpu,
+ target_cpu);
+
+ schedstat_inc(p, se.statistics.nr_wakeups_fbt_count);
+ schedstat_inc(this_rq(), eas_stats.fbt_count);
return target_cpu;
}
@@ -7483,7 +7686,7 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu)
static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync)
{
struct sched_domain *sd;
- int target_cpu = prev_cpu, tmp_target;
+ int target_cpu = prev_cpu, tmp_target, tmp_backup;
bool boosted, prefer_idle;
schedstat_inc(p, se.statistics.nr_wakeups_secb_attempts);
@@ -7508,9 +7711,11 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync
prefer_idle = 0;
#endif
+ sync_entity_load_avg(&p->se);
+
sd = rcu_dereference(per_cpu(sd_ea, prev_cpu));
/* Find a cpu with sufficient capacity */
- tmp_target = find_best_target(p, boosted, prefer_idle);
+ tmp_target = find_best_target(p, &tmp_backup, boosted, prefer_idle);
if (!sd)
goto unlock;
@@ -7539,10 +7744,15 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync
}
if (energy_diff(&eenv) >= 0) {
- schedstat_inc(p, se.statistics.nr_wakeups_secb_no_nrg_sav);
- schedstat_inc(this_rq(), eas_stats.secb_no_nrg_sav);
- target_cpu = prev_cpu;
- goto unlock;
+ /* No energy saving for target_cpu, try backup */
+ target_cpu = tmp_backup;
+ eenv.dst_cpu = target_cpu;
+ if (tmp_backup < 0 || energy_diff(&eenv) >= 0) {
+ schedstat_inc(p, se.statistics.nr_wakeups_secb_no_nrg_sav);
+ schedstat_inc(this_rq(), eas_stats.secb_no_nrg_sav);
+ target_cpu = prev_cpu;
+ goto unlock;
+ }
}
schedstat_inc(p, se.statistics.nr_wakeups_secb_nrg_sav);
@@ -7584,16 +7794,9 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f
return select_best_cpu(p, prev_cpu, 0, sync);
#endif
- if (sd_flag & SD_BALANCE_WAKE) {
- /*
- * do wake_cap unconditionally as it causes task and cpu
- * utilization to be synced, and we need that for energy
- * aware wakeups
- */
- int _wake_cap = wake_cap(p, cpu, prev_cpu);
- want_affine = !wake_wide(p) && !_wake_cap
+ if (sd_flag & SD_BALANCE_WAKE)
+ want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu)
&& cpumask_test_cpu(cpu, tsk_cpus_allowed(p));
- }
if (energy_aware() && !(cpu_rq(prev_cpu)->rd->overutilized))
return select_energy_cpu_brute(p, prev_cpu, sync);
@@ -9189,6 +9392,38 @@ group_type group_classify(struct sched_group *group,
return group_other;
}
+#ifdef CONFIG_NO_HZ_COMMON
+/*
+ * idle load balancing data
+ * - used by the nohz balance, but we want it available here
+ * so that we can see which CPUs have no tick.
+ */
+static struct {
+ cpumask_var_t idle_cpus_mask;
+ atomic_t nr_cpus;
+ unsigned long next_balance; /* in jiffy units */
+} nohz ____cacheline_aligned;
+
+static inline void update_cpu_stats_if_tickless(struct rq *rq)
+{
+ /* only called from update_sg_lb_stats when irqs are disabled */
+ if (cpumask_test_cpu(rq->cpu, nohz.idle_cpus_mask)) {
+ /* rate limit updates to once-per-jiffie at most */
+ if (READ_ONCE(jiffies) <= rq->last_load_update_tick)
+ return;
+
+ raw_spin_lock(&rq->lock);
+ update_rq_clock(rq);
+ update_idle_cpu_load(rq);
+ update_cfs_rq_load_avg(rq->clock_task, &rq->cfs, false);
+ raw_spin_unlock(&rq->lock);
+ }
+}
+
+#else
+static inline void update_cpu_stats_if_tickless(struct rq *rq) { }
+#endif
+
/**
* update_sg_lb_stats - Update sched_group's statistics for load balancing.
* @env: The load balancing environment.
@@ -9220,6 +9455,12 @@ static inline void update_sg_lb_stats(struct lb_env *env,
if (cpu_isolated(i))
continue;
+ /* if we are entering idle and there are CPUs with
+ * their tick stopped, do an update for them
+ */
+ if (env->idle == CPU_NEWLY_IDLE)
+ update_cpu_stats_if_tickless(rq);
+
/* Bias balancing toward cpus of our domain */
if (local_group)
load = target_load(i, load_idx);
@@ -10680,11 +10921,6 @@ static inline int on_null_domain(struct rq *rq)
* needed, they will kick the idle load balancer, which then does idle
* load balancing for all the idle CPUs.
*/
-static struct {
- cpumask_var_t idle_cpus_mask;
- atomic_t nr_cpus;
- unsigned long next_balance; /* in jiffy units */
-} nohz ____cacheline_aligned;
#ifdef CONFIG_SCHED_HMP
static inline int find_new_hmp_ilb(int type)
@@ -11111,6 +11347,10 @@ static inline int _nohz_kick_needed(struct rq *rq, int cpu, int *type)
(!energy_aware() || cpu_overutilized(cpu)))
return true;
+ /* Do idle load balance if there have misfit task */
+ if (energy_aware() && rq->misfit_task)
+ return 1;
+
return (rq->nr_running >= 2);
}
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index c03d51a017bf..ee095f4e7230 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1823,6 +1823,7 @@ static int find_lowest_rq_hmp(struct task_struct *task)
* the best one based on our affinity and topology.
*/
+retry:
for_each_sched_cluster(cluster) {
if (boost_on_big && cluster->capacity != max_possible_capacity)
continue;
@@ -1830,6 +1831,15 @@ static int find_lowest_rq_hmp(struct task_struct *task)
cpumask_and(&candidate_mask, &cluster->cpus, lowest_mask);
cpumask_andnot(&candidate_mask, &candidate_mask,
cpu_isolated_mask);
+ /*
+ * When placement boost is active, if there is no eligible CPU
+ * in the highest capacity cluster, we fallback to the other
+ * clusters. So clear the CPUs of the traversed cluster from
+ * the lowest_mask.
+ */
+ if (unlikely(boost_on_big))
+ cpumask_andnot(lowest_mask, lowest_mask,
+ &cluster->cpus);
if (cpumask_empty(&candidate_mask))
continue;
@@ -1869,6 +1879,11 @@ static int find_lowest_rq_hmp(struct task_struct *task)
break;
}
+ if (unlikely(boost_on_big && best_cpu == -1)) {
+ boost_on_big = 0;
+ goto retry;
+ }
+
return best_cpu;
}
#endif /* CONFIG_SCHED_HMP */
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 67b7da81f8a2..33bf0c07e757 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1767,7 +1767,11 @@ static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu)
* per-task data have been completed by this moment.
*/
smp_wmb();
+#ifdef CONFIG_THREAD_INFO_IN_TASK
+ p->cpu = cpu;
+#else
task_thread_info(p)->cpu = cpu;
+#endif
p->wake_cpu = cpu;
#endif
}
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index 6e053bd9830c..92c3aae8e056 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -72,7 +72,15 @@ static cpumask_t mpc_mask = CPU_MASK_ALL;
__read_mostly unsigned int walt_ravg_window = 20000000;
/* Min window size (in ns) = 10ms */
+#ifdef CONFIG_HZ_300
+/*
+ * Tick interval becomes to 3333333 due to
+ * rounding error when HZ=300.
+ */
+#define MIN_SCHED_RAVG_WINDOW (3333333 * 6)
+#else
#define MIN_SCHED_RAVG_WINDOW 10000000
+#endif
/* Max window size (in ns) = 1s */
#define MAX_SCHED_RAVG_WINDOW 1000000000
diff --git a/kernel/sched/walt.h b/kernel/sched/walt.h
index e181c87a928d..f56c4da16d0b 100644
--- a/kernel/sched/walt.h
+++ b/kernel/sched/walt.h
@@ -55,6 +55,8 @@ static inline void walt_migrate_sync_cpu(int cpu) { }
static inline void walt_init_cpu_efficiency(void) { }
static inline u64 walt_ktime_clock(void) { return 0; }
+#define walt_cpu_high_irqload(cpu) false
+
#endif /* CONFIG_SCHED_WALT */
extern unsigned int walt_disabled;
diff --git a/kernel/signal.c b/kernel/signal.c
index b92a047ddc82..5d50ea899b6d 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -346,7 +346,7 @@ static bool task_participate_group_stop(struct task_struct *task)
* fresh group stop. Read comment in do_signal_stop() for details.
*/
if (!sig->group_stop_count && !(sig->flags & SIGNAL_STOP_STOPPED)) {
- sig->flags = SIGNAL_STOP_STOPPED;
+ signal_set_stop_flags(sig, SIGNAL_STOP_STOPPED);
return true;
}
return false;
@@ -845,7 +845,7 @@ static bool prepare_signal(int sig, struct task_struct *p, bool force)
* will take ->siglock, notice SIGNAL_CLD_MASK, and
* notify its parent. See get_signal_to_deliver().
*/
- signal->flags = why | SIGNAL_STOP_CONTINUED;
+ signal_set_stop_flags(signal, why | SIGNAL_STOP_CONTINUED);
signal->group_stop_count = 0;
signal->group_exit_code = 0;
}
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 39ffd41594ce..9029227e5f57 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -234,6 +234,8 @@ static inline bool lockdep_softirq_start(void) { return false; }
static inline void lockdep_softirq_end(bool in_hardirq) { }
#endif
+#define long_softirq_pending() (local_softirq_pending() & LONG_SOFTIRQ_MASK)
+#define defer_for_rt() (long_softirq_pending() && cpupri_check_rt())
asmlinkage __visible void __do_softirq(void)
{
unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
@@ -297,6 +299,7 @@ restart:
pending = local_softirq_pending();
if (pending) {
if (time_before(jiffies, end) && !need_resched() &&
+ !defer_for_rt() &&
--max_restart)
goto restart;
@@ -349,7 +352,7 @@ void irq_enter(void)
static inline void invoke_softirq(void)
{
- if (!force_irqthreads) {
+ if (!force_irqthreads && !defer_for_rt()) {
#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
/*
* We can safely execute softirq on the current stack if
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f27d2ba78d14..8576e6385d63 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2380,9 +2380,12 @@ static int do_proc_douintvec_conv(bool *negp, unsigned long *lvalp,
if (write) {
if (*negp)
return -EINVAL;
+ if (*lvalp > UINT_MAX)
+ return -EINVAL;
*valp = *lvalp;
} else {
unsigned int val = *valp;
+ *negp = false;
*lvalp = (unsigned long)val;
}
return 0;
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 1a4de0022cc5..ceec77c652b5 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -848,7 +848,8 @@ static int alarm_timer_set(struct k_itimer *timr, int flags,
* Rate limit to the tick as a hot fix to prevent DOS. Will be
* mopped up later.
*/
- if (ktime_to_ns(timr->it.alarm.interval) < TICK_NSEC)
+ if (timr->it.alarm.interval.tv64 &&
+ ktime_to_ns(timr->it.alarm.interval) < TICK_NSEC)
timr->it.alarm.interval = ktime_set(0, TICK_NSEC);
exp = timespec_to_ktime(new_setting->it_value);
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index ec2102104cb8..333f627a3a3b 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -896,6 +896,18 @@ ktime_t tick_nohz_get_sleep_length(void)
return ts->sleep_length;
}
+/**
+ * tick_nohz_get_idle_calls - return the current idle calls counter value
+ *
+ * Called from the schedutil frequency scaling governor in scheduler context.
+ */
+unsigned long tick_nohz_get_idle_calls(void)
+{
+ struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
+
+ return ts->idle_calls;
+}
+
static void tick_nohz_account_idle_ticks(struct tick_sched *ts)
{
#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 34b2a0d5cf1a..eba904bae48c 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -3535,7 +3535,7 @@ match_records(struct ftrace_hash *hash, char *func, int len, char *mod)
int exclude_mod = 0;
int found = 0;
int ret;
- int clear_filter;
+ int clear_filter = 0;
if (func) {
func_g.type = filter_parse_regex(func, len, &func_g.search,
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 60d246c4eefa..a579a874045b 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1362,7 +1362,7 @@ static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED;
struct saved_cmdlines_buffer {
unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1];
unsigned *map_cmdline_to_pid;
- unsigned *saved_tgids;
+ unsigned *map_cmdline_to_tgid;
unsigned cmdline_num;
int cmdline_idx;
char *saved_cmdlines;
@@ -1396,9 +1396,10 @@ static int allocate_cmdlines_buffer(unsigned int val,
return -ENOMEM;
}
- s->saved_tgids = kmalloc_array(val, sizeof(*s->saved_tgids),
- GFP_KERNEL);
- if (!s->saved_tgids) {
+ s->map_cmdline_to_tgid = kmalloc_array(val,
+ sizeof(*s->map_cmdline_to_tgid),
+ GFP_KERNEL);
+ if (!s->map_cmdline_to_tgid) {
kfree(s->map_cmdline_to_pid);
kfree(s->saved_cmdlines);
return -ENOMEM;
@@ -1410,8 +1411,8 @@ static int allocate_cmdlines_buffer(unsigned int val,
sizeof(s->map_pid_to_cmdline));
memset(s->map_cmdline_to_pid, NO_CMDLINE_MAP,
val * sizeof(*s->map_cmdline_to_pid));
- memset(s->saved_tgids, 0,
- val * sizeof(*s->saved_tgids));
+ memset(s->map_cmdline_to_tgid, NO_CMDLINE_MAP,
+ val * sizeof(*s->map_cmdline_to_tgid));
return 0;
}
@@ -1577,14 +1578,17 @@ static int trace_save_cmdline(struct task_struct *tsk)
if (!tsk->pid || unlikely(tsk->pid > PID_MAX_DEFAULT))
return 0;
+ preempt_disable();
/*
* It's not the end of the world if we don't get
* the lock, but we also don't want to spin
* nor do we want to disable interrupts,
* so if we miss here, then better luck next time.
*/
- if (!arch_spin_trylock(&trace_cmdline_lock))
+ if (!arch_spin_trylock(&trace_cmdline_lock)) {
+ preempt_enable();
return 0;
+ }
idx = savedcmd->map_pid_to_cmdline[tsk->pid];
if (idx == NO_CMDLINE_MAP) {
@@ -1607,8 +1611,9 @@ static int trace_save_cmdline(struct task_struct *tsk)
}
set_cmdline(idx, tsk->comm);
- savedcmd->saved_tgids[idx] = tsk->tgid;
+ savedcmd->map_cmdline_to_tgid[idx] = tsk->tgid;
arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
return 1;
}
@@ -1650,19 +1655,29 @@ void trace_find_cmdline(int pid, char comm[])
preempt_enable();
}
-int trace_find_tgid(int pid)
+static int __find_tgid_locked(int pid)
{
unsigned map;
int tgid;
- preempt_disable();
- arch_spin_lock(&trace_cmdline_lock);
map = savedcmd->map_pid_to_cmdline[pid];
if (map != NO_CMDLINE_MAP)
- tgid = savedcmd->saved_tgids[map];
+ tgid = savedcmd->map_cmdline_to_tgid[map];
else
tgid = -1;
+ return tgid;
+}
+
+int trace_find_tgid(int pid)
+{
+ int tgid;
+
+ preempt_disable();
+ arch_spin_lock(&trace_cmdline_lock);
+
+ tgid = __find_tgid_locked(pid);
+
arch_spin_unlock(&trace_cmdline_lock);
preempt_enable();
@@ -3979,10 +3994,15 @@ tracing_saved_cmdlines_size_read(struct file *filp, char __user *ubuf,
{
char buf[64];
int r;
+ unsigned int n;
+ preempt_disable();
arch_spin_lock(&trace_cmdline_lock);
- r = scnprintf(buf, sizeof(buf), "%u\n", savedcmd->cmdline_num);
+ n = savedcmd->cmdline_num;
arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
+
+ r = scnprintf(buf, sizeof(buf), "%u\n", n);
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}
@@ -3991,7 +4011,7 @@ static void free_saved_cmdlines_buffer(struct saved_cmdlines_buffer *s)
{
kfree(s->saved_cmdlines);
kfree(s->map_cmdline_to_pid);
- kfree(s->saved_tgids);
+ kfree(s->map_cmdline_to_tgid);
kfree(s);
}
@@ -4008,10 +4028,12 @@ static int tracing_resize_saved_cmdlines(unsigned int val)
return -ENOMEM;
}
+ preempt_disable();
arch_spin_lock(&trace_cmdline_lock);
savedcmd_temp = savedcmd;
savedcmd = s;
arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
free_saved_cmdlines_buffer(savedcmd_temp);
return 0;
@@ -4230,33 +4252,61 @@ tracing_saved_tgids_read(struct file *file, char __user *ubuf,
char *file_buf;
char *buf;
int len = 0;
- int pid;
int i;
+ int *pids;
+ int n = 0;
- file_buf = kmalloc(savedcmd->cmdline_num*(16+1+16), GFP_KERNEL);
- if (!file_buf)
- return -ENOMEM;
+ preempt_disable();
+ arch_spin_lock(&trace_cmdline_lock);
- buf = file_buf;
+ pids = kmalloc_array(savedcmd->cmdline_num, 2*sizeof(int), GFP_KERNEL);
+ if (!pids) {
+ arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
+ return -ENOMEM;
+ }
for (i = 0; i < savedcmd->cmdline_num; i++) {
- int tgid;
- int r;
+ int pid;
pid = savedcmd->map_cmdline_to_pid[i];
if (pid == -1 || pid == NO_CMDLINE_MAP)
continue;
- tgid = trace_find_tgid(pid);
- r = sprintf(buf, "%d %d\n", pid, tgid);
+ pids[n] = pid;
+ pids[n+1] = __find_tgid_locked(pid);
+ n += 2;
+ }
+ arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
+
+ if (n == 0) {
+ kfree(pids);
+ return 0;
+ }
+
+ /* enough to hold max pair of pids + space, lr and nul */
+ len = n * 12;
+ file_buf = kmalloc(len, GFP_KERNEL);
+ if (!file_buf) {
+ kfree(pids);
+ return -ENOMEM;
+ }
+
+ buf = file_buf;
+ for (i = 0; i < n && len > 0; i += 2) {
+ int r;
+
+ r = snprintf(buf, len, "%d %d\n", pids[i], pids[i+1]);
buf += r;
- len += r;
+ len -= r;
}
len = simple_read_from_buffer(ubuf, cnt, ppos,
- file_buf, len);
+ file_buf, buf - file_buf);
kfree(file_buf);
+ kfree(pids);
return len;
}
@@ -6847,6 +6897,7 @@ static int instance_rmdir(const char *name)
}
kfree(tr->topts);
+ free_cpumask_var(tr->tracing_cpumask);
kfree(tr->name);
kfree(tr);
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 6816302542b2..f0e5408499b6 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -1979,6 +1979,10 @@ static int create_filter(struct trace_event_call *call,
if (err && set_str)
append_filter_err(ps, filter);
}
+ if (err && !set_str) {
+ free_event_filter(filter);
+ filter = NULL;
+ }
create_filter_finish(ps);
*filterp = filter;
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 12ea4ea619ee..e9092a0247bf 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -659,30 +659,25 @@ static int create_trace_kprobe(int argc, char **argv)
pr_info("Probe point is not specified.\n");
return -EINVAL;
}
- if (isdigit(argv[1][0])) {
- if (is_return) {
- pr_info("Return probe point must be a symbol.\n");
- return -EINVAL;
- }
- /* an address specified */
- ret = kstrtoul(&argv[1][0], 0, (unsigned long *)&addr);
- if (ret) {
- pr_info("Failed to parse address.\n");
- return ret;
- }
- } else {
+
+ /* try to parse an address. if that fails, try to read the
+ * input as a symbol. */
+ if (kstrtoul(argv[1], 0, (unsigned long *)&addr)) {
/* a symbol specified */
symbol = argv[1];
/* TODO: support .init module functions */
ret = traceprobe_split_symbol_offset(symbol, &offset);
if (ret) {
- pr_info("Failed to parse symbol.\n");
+ pr_info("Failed to parse either an address or a symbol.\n");
return ret;
}
if (offset && is_return) {
pr_info("Return probe must be used without offset.\n");
return -EINVAL;
}
+ } else if (is_return) {
+ pr_info("Return probe point must be a symbol.\n");
+ return -EINVAL;
}
argc -= 2; argv += 2;
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 73c018d7df00..80b5dbfd187d 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -3669,8 +3669,12 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq,
return -EINVAL;
/* creating multiple pwqs breaks ordering guarantee */
- if (WARN_ON((wq->flags & __WQ_ORDERED) && !list_empty(&wq->pwqs)))
- return -EINVAL;
+ if (!list_empty(&wq->pwqs)) {
+ if (WARN_ON(wq->flags & __WQ_ORDERED_EXPLICIT))
+ return -EINVAL;
+
+ wq->flags &= ~__WQ_ORDERED;
+ }
ctx = apply_wqattrs_prepare(wq, attrs);
@@ -3856,6 +3860,16 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
struct workqueue_struct *wq;
struct pool_workqueue *pwq;
+ /*
+ * Unbound && max_active == 1 used to imply ordered, which is no
+ * longer the case on NUMA machines due to per-node pools. While
+ * alloc_ordered_workqueue() is the right way to create an ordered
+ * workqueue, keep the previous behavior to avoid subtle breakages
+ * on NUMA.
+ */
+ if ((flags & WQ_UNBOUND) && max_active == 1)
+ flags |= __WQ_ORDERED;
+
/* see the comment above the definition of WQ_POWER_EFFICIENT */
if ((flags & WQ_POWER_EFFICIENT) && wq_power_efficient)
flags |= WQ_UNBOUND;
@@ -4044,13 +4058,14 @@ void workqueue_set_max_active(struct workqueue_struct *wq, int max_active)
struct pool_workqueue *pwq;
/* disallow meddling with max_active for ordered workqueues */
- if (WARN_ON(wq->flags & __WQ_ORDERED))
+ if (WARN_ON(wq->flags & __WQ_ORDERED_EXPLICIT))
return;
max_active = wq_clamp_max_active(max_active, wq->flags, wq->name);
mutex_lock(&wq->mutex);
+ wq->flags &= ~__WQ_ORDERED;
wq->saved_max_active = max_active;
for_each_pwq(pwq, wq)
@@ -5178,7 +5193,7 @@ int workqueue_sysfs_register(struct workqueue_struct *wq)
* attributes breaks ordering guarantee. Disallow exposing ordered
* workqueues.
*/
- if (WARN_ON(wq->flags & __WQ_ORDERED))
+ if (WARN_ON(wq->flags & __WQ_ORDERED_EXPLICIT))
return -EINVAL;
wq->wq_dev = wq_dev = kzalloc(sizeof(*wq_dev), GFP_KERNEL);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 6da96c4f98a5..23b74fd4e28f 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -145,7 +145,7 @@ config DEBUG_INFO_REDUCED
config DEBUG_INFO_SPLIT
bool "Produce split debuginfo in .dwo files"
- depends on DEBUG_INFO
+ depends on DEBUG_INFO && !FRV
help
Generate debug info into separate .dwo files. This significantly
reduces the build directory size for builds with DEBUG_INFO,
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/mm/internal.h b/mm/internal.h
index 46d27f378885..e17af58d2bf7 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -454,6 +454,7 @@ struct tlbflush_unmap_batch;
#ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
void try_to_unmap_flush(void);
void try_to_unmap_flush_dirty(void);
+void flush_tlb_batched_pending(struct mm_struct *mm);
#else
static inline void try_to_unmap_flush(void)
{
@@ -461,6 +462,8 @@ static inline void try_to_unmap_flush(void)
static inline void try_to_unmap_flush_dirty(void)
{
}
-
+static inline void flush_tlb_batched_pending(struct mm_struct *mm)
+{
+}
#endif /* CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH */
#endif /* __MM_INTERNAL_H */
diff --git a/mm/memory.c b/mm/memory.c
index 8689df9b09d5..d6e10c888541 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1127,6 +1127,7 @@ again:
init_rss_vec(rss);
start_pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
pte = start_pte;
+ flush_tlb_batched_pending(mm);
arch_enter_lazy_mmu_mode();
do {
pte_t ptent = *pte;
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index d56142b66171..177668a9c267 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -895,11 +895,6 @@ static long do_get_mempolicy(int *policy, nodemask_t *nmask,
*policy |= (pol->flags & MPOL_MODE_FLAGS);
}
- if (vma) {
- up_read(&current->mm->mmap_sem);
- vma = NULL;
- }
-
err = 0;
if (nmask) {
if (mpol_store_user_nodemask(pol)) {
diff --git a/mm/mempool.c b/mm/mempool.c
index 004d42b1dfaf..7924f4f58a6d 100644
--- a/mm/mempool.c
+++ b/mm/mempool.c
@@ -135,8 +135,8 @@ static void *remove_element(mempool_t *pool)
void *element = pool->elements[--pool->curr_nr];
BUG_ON(pool->curr_nr < 0);
- check_element(pool, element);
kasan_unpoison_element(pool, element);
+ check_element(pool, element);
return element;
}
diff --git a/mm/migrate.c b/mm/migrate.c
index 85af2816b6d2..a021071eceaf 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -40,6 +40,7 @@
#include <linux/mmu_notifier.h>
#include <linux/page_idle.h>
#include <linux/page_owner.h>
+#include <linux/ptrace.h>
#include <asm/tlbflush.h>
@@ -1649,7 +1650,6 @@ SYSCALL_DEFINE6(move_pages, pid_t, pid, unsigned long, nr_pages,
const int __user *, nodes,
int __user *, status, int, flags)
{
- const struct cred *cred = current_cred(), *tcred;
struct task_struct *task;
struct mm_struct *mm;
int err;
@@ -1673,14 +1673,9 @@ SYSCALL_DEFINE6(move_pages, pid_t, pid, unsigned long, nr_pages,
/*
* Check if this process has the right to modify the specified
- * process. The right exists if the process has administrative
- * capabilities, superuser privileges or the same
- * userid as the target process.
+ * process. Use the regular "ptrace_may_access()" checks.
*/
- tcred = __task_cred(task);
- if (!uid_eq(cred->euid, tcred->suid) && !uid_eq(cred->euid, tcred->uid) &&
- !uid_eq(cred->uid, tcred->suid) && !uid_eq(cred->uid, tcred->uid) &&
- !capable(CAP_SYS_NICE)) {
+ if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) {
rcu_read_unlock();
err = -EPERM;
goto out;
diff --git a/mm/mmap.c b/mm/mmap.c
index 092729c2cb72..16743bf76a88 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2206,7 +2206,7 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
/* Guard against exceeding limits of the address space. */
address &= PAGE_MASK;
- if (address >= TASK_SIZE)
+ if (address >= (TASK_SIZE & PAGE_MASK))
return -ENOMEM;
address += PAGE_SIZE;
diff --git a/mm/mprotect.c b/mm/mprotect.c
index bddb2c75492d..b8849a3930a0 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -72,6 +72,7 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
if (!pte)
return 0;
+ flush_tlb_batched_pending(vma->vm_mm);
arch_enter_lazy_mmu_mode();
do {
oldpte = *pte;
diff --git a/mm/mremap.c b/mm/mremap.c
index c25bc6268e46..fe7b7f65f4f4 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -135,6 +135,7 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd,
new_ptl = pte_lockptr(mm, new_pmd);
if (new_ptl != old_ptl)
spin_lock_nested(new_ptl, SINGLE_DEPTH_NESTING);
+ flush_tlb_batched_pending(vma->vm_mm);
arch_enter_lazy_mmu_mode();
for (; old_addr < old_end; old_pte++, old_addr += PAGE_SIZE,
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 6759192e69de..915c60258935 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1579,14 +1579,14 @@ int move_freepages(struct zone *zone,
#endif
for (page = start_page; page <= end_page;) {
- /* Make sure we are not inadvertently changing nodes */
- VM_BUG_ON_PAGE(page_to_nid(page) != zone_to_nid(zone), page);
-
if (!pfn_valid_within(page_to_pfn(page))) {
page++;
continue;
}
+ /* Make sure we are not inadvertently changing nodes */
+ VM_BUG_ON_PAGE(page_to_nid(page) != zone_to_nid(zone), page);
+
if (!PageBuddy(page)) {
page++;
continue;
@@ -5953,8 +5953,8 @@ unsigned long free_reserved_area(void *start, void *end, int poison, char *s)
}
if (pages && s)
- pr_info("Freeing %s memory: %ldK (%p - %p)\n",
- s, pages << (PAGE_SHIFT - 10), start, end);
+ pr_info("Freeing %s memory: %ldK\n",
+ s, pages << (PAGE_SHIFT - 10));
return pages;
}
@@ -6910,7 +6910,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
/* Make sure the range is really isolated. */
if (test_pages_isolated(outer_start, end, false)) {
- pr_info("%s: [%lx, %lx) PFNs busy\n",
+ pr_info_ratelimited("%s: [%lx, %lx) PFNs busy\n",
__func__, outer_start, end);
ret = -EBUSY;
goto done;
diff --git a/mm/rmap.c b/mm/rmap.c
index 59489b3b1ac6..cbaf273b0f97 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -649,6 +649,13 @@ static void set_tlb_ubc_flush_pending(struct mm_struct *mm,
tlb_ubc->flush_required = true;
/*
+ * Ensure compiler does not re-order the setting of tlb_flush_batched
+ * before the PTE is cleared.
+ */
+ barrier();
+ mm->tlb_flush_batched = true;
+
+ /*
* If the PTE was dirty then it's best to assume it's writable. The
* caller must use try_to_unmap_flush_dirty() or try_to_unmap_flush()
* before the page is queued for IO.
@@ -675,6 +682,35 @@ static bool should_defer_flush(struct mm_struct *mm, enum ttu_flags flags)
return should_defer;
}
+
+/*
+ * Reclaim unmaps pages under the PTL but do not flush the TLB prior to
+ * releasing the PTL if TLB flushes are batched. It's possible for a parallel
+ * operation such as mprotect or munmap to race between reclaim unmapping
+ * the page and flushing the page. If this race occurs, it potentially allows
+ * access to data via a stale TLB entry. Tracking all mm's that have TLB
+ * batching in flight would be expensive during reclaim so instead track
+ * whether TLB batching occurred in the past and if so then do a flush here
+ * if required. This will cost one additional flush per reclaim cycle paid
+ * by the first operation at risk such as mprotect and mumap.
+ *
+ * This must be called under the PTL so that an access to tlb_flush_batched
+ * that is potentially a "reclaim vs mprotect/munmap/etc" race will synchronise
+ * via the PTL.
+ */
+void flush_tlb_batched_pending(struct mm_struct *mm)
+{
+ if (mm->tlb_flush_batched) {
+ flush_tlb_mm(mm);
+
+ /*
+ * Do not allow the compiler to re-order the clearing of
+ * tlb_flush_batched before the tlb is flushed.
+ */
+ barrier();
+ mm->tlb_flush_batched = false;
+ }
+}
#else
static void set_tlb_ubc_flush_pending(struct mm_struct *mm,
struct page *page, bool writable)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 94fecacf0ddc..5f6e29f25af9 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2676,7 +2676,7 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
if (!populated_zone(zone))
continue;
- classzone_idx = requested_highidx;
+ classzone_idx = gfp_zone(sc->gfp_mask);
while (!populated_zone(zone->zone_pgdat->node_zones +
classzone_idx))
classzone_idx--;
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index e20ae2d3c498..5e4199d5a388 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -292,6 +292,10 @@ static void vlan_sync_address(struct net_device *dev,
if (ether_addr_equal(vlan->real_dev_addr, dev->dev_addr))
return;
+ /* vlan continues to inherit address of lower device */
+ if (vlan_dev_inherit_address(vlandev, dev))
+ goto out;
+
/* vlan address was different from the old address and is equal to
* the new address */
if (!ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) &&
@@ -304,6 +308,7 @@ static void vlan_sync_address(struct net_device *dev,
!ether_addr_equal(vlandev->dev_addr, dev->dev_addr))
dev_uc_add(dev, vlandev->dev_addr);
+out:
ether_addr_copy(vlan->real_dev_addr, dev->dev_addr);
}
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h
index 9d010a09ab98..cc1557978066 100644
--- a/net/8021q/vlan.h
+++ b/net/8021q/vlan.h
@@ -109,6 +109,8 @@ int vlan_check_real_dev(struct net_device *real_dev,
void vlan_setup(struct net_device *dev);
int register_vlan_dev(struct net_device *dev);
void unregister_vlan_dev(struct net_device *dev, struct list_head *head);
+bool vlan_dev_inherit_address(struct net_device *dev,
+ struct net_device *real_dev);
static inline u32 vlan_get_ingress_priority(struct net_device *dev,
u16 vlan_tci)
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index fded86508117..ca4dc9031073 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -244,6 +244,17 @@ void vlan_dev_get_realdev_name(const struct net_device *dev, char *result)
strncpy(result, vlan_dev_priv(dev)->real_dev->name, 23);
}
+bool vlan_dev_inherit_address(struct net_device *dev,
+ struct net_device *real_dev)
+{
+ if (dev->addr_assign_type != NET_ADDR_STOLEN)
+ return false;
+
+ ether_addr_copy(dev->dev_addr, real_dev->dev_addr);
+ call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+ return true;
+}
+
static int vlan_dev_open(struct net_device *dev)
{
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
@@ -254,7 +265,8 @@ static int vlan_dev_open(struct net_device *dev)
!(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
return -ENETDOWN;
- if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) {
+ if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr) &&
+ !vlan_dev_inherit_address(dev, real_dev)) {
err = dev_uc_add(real_dev, dev->dev_addr);
if (err < 0)
goto out;
@@ -558,8 +570,10 @@ static int vlan_dev_init(struct net_device *dev)
/* ipv6 shared card related stuff */
dev->dev_id = real_dev->dev_id;
- if (is_zero_ether_addr(dev->dev_addr))
- eth_hw_addr_inherit(dev, real_dev);
+ if (is_zero_ether_addr(dev->dev_addr)) {
+ ether_addr_copy(dev->dev_addr, real_dev->dev_addr);
+ dev->addr_assign_type = NET_ADDR_STOLEN;
+ }
if (is_zero_ether_addr(dev->broadcast))
memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len);
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index abeccb56fbbd..b08d36c952dd 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -484,16 +484,16 @@ static int bnep_session(void *arg)
struct net_device *dev = s->dev;
struct sock *sk = s->sock->sk;
struct sk_buff *skb;
- wait_queue_t wait;
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
BT_DBG("");
set_user_nice(current, -15);
- init_waitqueue_entry(&wait, current);
add_wait_queue(sk_sleep(sk), &wait);
while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
+ /* Ensure session->terminate is updated */
+ smp_mb__before_atomic();
if (atomic_read(&s->terminate))
break;
@@ -515,9 +515,8 @@ static int bnep_session(void *arg)
break;
netif_wake_queue(dev);
- schedule();
+ wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
- __set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
/* Cleanup session */
@@ -663,7 +662,7 @@ int bnep_del_connection(struct bnep_conndel_req *req)
s = __bnep_get_session(req->dst);
if (s) {
atomic_inc(&s->terminate);
- wake_up_process(s->task);
+ wake_up_interruptible(sk_sleep(s->sock->sk));
} else
err = -ENOENT;
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 011747337858..77f73bfa840b 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -281,16 +281,16 @@ static int cmtp_session(void *arg)
struct cmtp_session *session = arg;
struct sock *sk = session->sock->sk;
struct sk_buff *skb;
- wait_queue_t wait;
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
BT_DBG("session %pK", session);
set_user_nice(current, -15);
- init_waitqueue_entry(&wait, current);
add_wait_queue(sk_sleep(sk), &wait);
while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
+ /* Ensure session->terminate is updated */
+ smp_mb__before_atomic();
if (atomic_read(&session->terminate))
break;
@@ -307,9 +307,8 @@ static int cmtp_session(void *arg)
cmtp_process_transmit(session);
- schedule();
+ wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
- __set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
down_write(&cmtp_session_sem);
@@ -394,7 +393,7 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
err = cmtp_attach_device(session);
if (err < 0) {
atomic_inc(&session->terminate);
- wake_up_process(session->task);
+ wake_up_interruptible(sk_sleep(session->sock->sk));
up_write(&cmtp_session_sem);
return err;
}
@@ -432,7 +431,11 @@ int cmtp_del_connection(struct cmtp_conndel_req *req)
/* Stop session thread */
atomic_inc(&session->terminate);
- wake_up_process(session->task);
+
+ /* Ensure session->terminate is updated */
+ smp_mb__after_atomic();
+
+ wake_up_interruptible(sk_sleep(session->sock->sk));
} else
err = -ENOENT;
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index f02ffe558a08..f64de569175a 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -36,6 +36,7 @@
#define VERSION "1.2"
static DECLARE_RWSEM(hidp_session_sem);
+static DECLARE_WAIT_QUEUE_HEAD(hidp_session_wq);
static LIST_HEAD(hidp_session_list);
static unsigned char hidp_keycode[256] = {
@@ -1069,12 +1070,12 @@ static int hidp_session_start_sync(struct hidp_session *session)
* Wake up session thread and notify it to stop. This is asynchronous and
* returns immediately. Call this whenever a runtime error occurs and you want
* the session to stop.
- * Note: wake_up_process() performs any necessary memory-barriers for us.
+ * Note: wake_up_interruptible() performs any necessary memory-barriers for us.
*/
static void hidp_session_terminate(struct hidp_session *session)
{
atomic_inc(&session->terminate);
- wake_up_process(session->task);
+ wake_up_interruptible(&hidp_session_wq);
}
/*
@@ -1181,7 +1182,9 @@ static void hidp_session_run(struct hidp_session *session)
struct sock *ctrl_sk = session->ctrl_sock->sk;
struct sock *intr_sk = session->intr_sock->sk;
struct sk_buff *skb;
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
+ add_wait_queue(&hidp_session_wq, &wait);
for (;;) {
/*
* This thread can be woken up two ways:
@@ -1189,12 +1192,10 @@ static void hidp_session_run(struct hidp_session *session)
* session->terminate flag and wakes this thread up.
* - Via modifying the socket state of ctrl/intr_sock. This
* thread is woken up by ->sk_state_changed().
- *
- * Note: set_current_state() performs any necessary
- * memory-barriers for us.
*/
- set_current_state(TASK_INTERRUPTIBLE);
+ /* Ensure session->terminate is updated */
+ smp_mb__before_atomic();
if (atomic_read(&session->terminate))
break;
@@ -1228,11 +1229,22 @@ static void hidp_session_run(struct hidp_session *session)
hidp_process_transmit(session, &session->ctrl_transmit,
session->ctrl_sock);
- schedule();
+ wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
+ remove_wait_queue(&hidp_session_wq, &wait);
atomic_inc(&session->terminate);
- set_current_state(TASK_RUNNING);
+
+ /* Ensure session->terminate is updated */
+ smp_mb__after_atomic();
+}
+
+static int hidp_session_wake_function(wait_queue_t *wait,
+ unsigned int mode,
+ int sync, void *key)
+{
+ wake_up_interruptible(&hidp_session_wq);
+ return false;
}
/*
@@ -1245,7 +1257,8 @@ static void hidp_session_run(struct hidp_session *session)
static int hidp_session_thread(void *arg)
{
struct hidp_session *session = arg;
- wait_queue_t ctrl_wait, intr_wait;
+ DEFINE_WAIT_FUNC(ctrl_wait, hidp_session_wake_function);
+ DEFINE_WAIT_FUNC(intr_wait, hidp_session_wake_function);
BT_DBG("session %pK", session);
@@ -1255,8 +1268,6 @@ static int hidp_session_thread(void *arg)
set_user_nice(current, -15);
hidp_set_timer(session);
- init_waitqueue_entry(&ctrl_wait, current);
- init_waitqueue_entry(&intr_wait, current);
add_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait);
add_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait);
/* This memory barrier is paired with wq_has_sleeper(). See
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 6f2c704e41ab..b166b3e441a5 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -23,6 +23,7 @@
#include <linux/debugfs.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
+#include <crypto/algapi.h>
#include <crypto/b128ops.h>
#include <net/bluetooth/bluetooth.h>
@@ -524,7 +525,7 @@ bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
if (err)
return false;
- return !memcmp(bdaddr->b, hash, 3);
+ return !crypto_memneq(bdaddr->b, hash, 3);
}
int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa)
@@ -577,7 +578,7 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16])
/* This is unlikely, but we need to check that
* we didn't accidentially generate a debug key.
*/
- if (memcmp(smp->local_sk, debug_sk, 32))
+ if (crypto_memneq(smp->local_sk, debug_sk, 32))
break;
}
smp->debug_key = false;
@@ -991,7 +992,7 @@ static u8 smp_random(struct smp_chan *smp)
if (ret)
return SMP_UNSPECIFIED;
- if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) {
+ if (crypto_memneq(smp->pcnf, confirm, sizeof(smp->pcnf))) {
BT_ERR("Pairing failed (confirmation values mismatch)");
return SMP_CONFIRM_FAILED;
}
@@ -1491,7 +1492,7 @@ static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op)
smp->rrnd, r, cfm))
return SMP_UNSPECIFIED;
- if (memcmp(smp->pcnf, cfm, 16))
+ if (crypto_memneq(smp->pcnf, cfm, 16))
return SMP_CONFIRM_FAILED;
smp->passkey_round++;
@@ -1875,7 +1876,7 @@ static u8 sc_send_public_key(struct smp_chan *smp)
/* This is unlikely, but we need to check that
* we didn't accidentially generate a debug key.
*/
- if (memcmp(smp->local_sk, debug_sk, 32))
+ if (crypto_memneq(smp->local_sk, debug_sk, 32))
break;
}
}
@@ -2140,7 +2141,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
if (err)
return SMP_UNSPECIFIED;
- if (memcmp(smp->pcnf, cfm, 16))
+ if (crypto_memneq(smp->pcnf, cfm, 16))
return SMP_CONFIRM_FAILED;
} else {
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
@@ -2621,7 +2622,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
if (err)
return SMP_UNSPECIFIED;
- if (memcmp(cfm.confirm_val, smp->pcnf, 16))
+ if (crypto_memneq(cfm.confirm_val, smp->pcnf, 16))
return SMP_CONFIRM_FAILED;
}
@@ -2654,7 +2655,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
else
hcon->pending_sec_level = BT_SECURITY_FIPS;
- if (!memcmp(debug_pk, smp->remote_pk, 64))
+ if (!crypto_memneq(debug_pk, smp->remote_pk, 64))
set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
if (smp->method == DSP_PASSKEY) {
@@ -2753,7 +2754,7 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
if (err)
return SMP_UNSPECIFIED;
- if (memcmp(check->e, e, 16))
+ if (crypto_memneq(check->e, e, 16))
return SMP_DHKEY_CHECK_FAILED;
if (!hcon->out) {
@@ -3463,7 +3464,7 @@ static int __init test_ah(struct crypto_blkcipher *tfm_aes)
if (err)
return err;
- if (memcmp(res, exp, 3))
+ if (crypto_memneq(res, exp, 3))
return -EINVAL;
return 0;
@@ -3493,7 +3494,7 @@ static int __init test_c1(struct crypto_blkcipher *tfm_aes)
if (err)
return err;
- if (memcmp(res, exp, 16))
+ if (crypto_memneq(res, exp, 16))
return -EINVAL;
return 0;
@@ -3518,7 +3519,7 @@ static int __init test_s1(struct crypto_blkcipher *tfm_aes)
if (err)
return err;
- if (memcmp(res, exp, 16))
+ if (crypto_memneq(res, exp, 16))
return -EINVAL;
return 0;
@@ -3550,7 +3551,7 @@ static int __init test_f4(struct crypto_hash *tfm_cmac)
if (err)
return err;
- if (memcmp(res, exp, 16))
+ if (crypto_memneq(res, exp, 16))
return -EINVAL;
return 0;
@@ -3584,10 +3585,10 @@ static int __init test_f5(struct crypto_hash *tfm_cmac)
if (err)
return err;
- if (memcmp(mackey, exp_mackey, 16))
+ if (crypto_memneq(mackey, exp_mackey, 16))
return -EINVAL;
- if (memcmp(ltk, exp_ltk, 16))
+ if (crypto_memneq(ltk, exp_ltk, 16))
return -EINVAL;
return 0;
@@ -3620,7 +3621,7 @@ static int __init test_f6(struct crypto_hash *tfm_cmac)
if (err)
return err;
- if (memcmp(res, exp, 16))
+ if (crypto_memneq(res, exp, 16))
return -EINVAL;
return 0;
@@ -3674,7 +3675,7 @@ static int __init test_h6(struct crypto_hash *tfm_cmac)
if (err)
return err;
- if (memcmp(res, exp, 16))
+ if (crypto_memneq(res, exp, 16))
return -EINVAL;
return 0;
diff --git a/net/core/dev.c b/net/core/dev.c
index 9a3aaba15c5a..ccc6fe0e5ca1 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2552,9 +2552,10 @@ EXPORT_SYMBOL(skb_mac_gso_segment);
static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path)
{
if (tx_path)
- return skb->ip_summed != CHECKSUM_PARTIAL;
- else
- return skb->ip_summed == CHECKSUM_NONE;
+ return skb->ip_summed != CHECKSUM_PARTIAL &&
+ skb->ip_summed != CHECKSUM_UNNECESSARY;
+
+ return skb->ip_summed == CHECKSUM_NONE;
}
/**
@@ -2573,11 +2574,12 @@ static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path)
struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
netdev_features_t features, bool tx_path)
{
+ struct sk_buff *segs;
+
if (unlikely(skb_needs_check(skb, tx_path))) {
int err;
- skb_warn_bad_offload(skb);
-
+ /* We're going to init ->check field in TCP or UDP header */
err = skb_cow_head(skb, 0);
if (err < 0)
return ERR_PTR(err);
@@ -2592,7 +2594,12 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
- return skb_mac_gso_segment(skb, features);
+ segs = skb_mac_gso_segment(skb, features);
+
+ if (unlikely(skb_needs_check(skb, tx_path)))
+ skb_warn_bad_offload(skb);
+
+ return segs;
}
EXPORT_SYMBOL(__skb_gso_segment);
@@ -4383,6 +4390,12 @@ struct packet_offload *gro_find_complete_by_type(__be16 type)
}
EXPORT_SYMBOL(gro_find_complete_by_type);
+static void napi_skb_free_stolen_head(struct sk_buff *skb)
+{
+ skb_dst_drop(skb);
+ kmem_cache_free(skbuff_head_cache, skb);
+}
+
static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
{
switch (ret) {
@@ -4396,12 +4409,10 @@ static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
break;
case GRO_MERGED_FREE:
- if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD) {
- skb_dst_drop(skb);
- kmem_cache_free(skbuff_head_cache, skb);
- } else {
+ if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD)
+ napi_skb_free_stolen_head(skb);
+ else
__kfree_skb(skb);
- }
break;
case GRO_HELD:
@@ -4467,10 +4478,16 @@ static gro_result_t napi_frags_finish(struct napi_struct *napi,
break;
case GRO_DROP:
- case GRO_MERGED_FREE:
napi_reuse_skb(napi, skb);
break;
+ case GRO_MERGED_FREE:
+ if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD)
+ napi_skb_free_stolen_head(skb);
+ else
+ napi_reuse_skb(napi, skb);
+ break;
+
case GRO_MERGED:
break;
}
@@ -7088,8 +7105,8 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
} else {
netdev_stats_to_stats64(storage, &dev->stats);
}
- storage->rx_dropped += atomic_long_read(&dev->rx_dropped);
- storage->tx_dropped += atomic_long_read(&dev->tx_dropped);
+ storage->rx_dropped += (unsigned long)atomic_long_read(&dev->rx_dropped);
+ storage->tx_dropped += (unsigned long)atomic_long_read(&dev->tx_dropped);
return storage;
}
EXPORT_SYMBOL(dev_get_stats);
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index b94b1d293506..151e047ce072 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -28,6 +28,7 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
return -EFAULT;
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
error = netdev_get_name(net, ifr.ifr_name, ifr.ifr_ifindex);
if (error)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 2ec5324a7ff7..5b3d611d8b5f 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1742,7 +1742,8 @@ static int do_setlink(const struct sk_buff *skb,
struct sockaddr *sa;
int len;
- len = sizeof(sa_family_t) + dev->addr_len;
+ len = sizeof(sa_family_t) + max_t(size_t, dev->addr_len,
+ sizeof(*sa));
sa = kmalloc(len, GFP_KERNEL);
if (!sa) {
err = -ENOMEM;
diff --git a/net/dccp/feat.c b/net/dccp/feat.c
index 1704948e6a12..f227f002c73d 100644
--- a/net/dccp/feat.c
+++ b/net/dccp/feat.c
@@ -1471,9 +1471,12 @@ int dccp_feat_init(struct sock *sk)
* singleton values (which always leads to failure).
* These settings can still (later) be overridden via sockopts.
*/
- if (ccid_get_builtin_ccids(&tx.val, &tx.len) ||
- ccid_get_builtin_ccids(&rx.val, &rx.len))
+ if (ccid_get_builtin_ccids(&tx.val, &tx.len))
return -ENOBUFS;
+ if (ccid_get_builtin_ccids(&rx.val, &rx.len)) {
+ kfree(tx.val);
+ return -ENOBUFS;
+ }
if (!dccp_feat_prefer(sysctl_dccp_tx_ccid, tx.val, tx.len) ||
!dccp_feat_prefer(sysctl_dccp_rx_ccid, rx.val, rx.len))
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 6467bf392e1b..e217f17997a4 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -635,6 +635,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
goto drop_and_free;
inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
+ reqsk_put(req);
return 0;
drop_and_free:
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 3470ad1843bb..09a9ab65f4e1 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -376,6 +376,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
goto drop_and_free;
inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
+ reqsk_put(req);
return 0;
drop_and_free:
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 9fe25bf63296..b68168fcc06a 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -24,6 +24,7 @@
#include <net/checksum.h>
#include <net/inet_sock.h>
+#include <net/inet_common.h>
#include <net/sock.h>
#include <net/xfrm.h>
@@ -170,6 +171,15 @@ const char *dccp_packet_name(const int type)
EXPORT_SYMBOL_GPL(dccp_packet_name);
+static void dccp_sk_destruct(struct sock *sk)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+
+ ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
+ dp->dccps_hc_tx_ccid = NULL;
+ inet_sock_destruct(sk);
+}
+
int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
{
struct dccp_sock *dp = dccp_sk(sk);
@@ -179,6 +189,7 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
icsk->icsk_syn_retries = sysctl_dccp_request_retries;
sk->sk_state = DCCP_CLOSED;
sk->sk_write_space = dccp_write_space;
+ sk->sk_destruct = dccp_sk_destruct;
icsk->icsk_sync_mss = dccp_sync_mss;
dp->dccps_mss_cache = 536;
dp->dccps_rate_last = jiffies;
@@ -201,10 +212,7 @@ void dccp_destroy_sock(struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
- /*
- * DCCP doesn't use sk_write_queue, just sk_send_head
- * for retransmissions
- */
+ __skb_queue_purge(&sk->sk_write_queue);
if (sk->sk_send_head != NULL) {
kfree_skb(sk->sk_send_head);
sk->sk_send_head = NULL;
@@ -222,8 +230,7 @@ void dccp_destroy_sock(struct sock *sk)
dp->dccps_hc_rx_ackvec = NULL;
}
ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
- ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
- dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
+ dp->dccps_hc_rx_ccid = NULL;
/* clean up feature negotiation state */
dccp_feat_list_purge(&dp->dccps_featneg);
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 7c4c881a7187..ee94bd32d6dc 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -1320,13 +1320,14 @@ static struct pernet_operations fib_net_ops = {
void __init ip_fib_init(void)
{
- rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);
- rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);
- rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);
+ fib_trie_init();
register_pernet_subsys(&fib_net_ops);
+
register_netdevice_notifier(&fib_netdev_notifier);
register_inetaddr_notifier(&fib_inetaddr_notifier);
- fib_trie_init();
+ rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);
+ rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);
+ rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);
}
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index b2504712259f..313e3c11a15a 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -1044,15 +1044,17 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
if (!fi)
goto failure;
- fib_info_cnt++;
if (cfg->fc_mx) {
fi->fib_metrics = kzalloc(sizeof(*fi->fib_metrics), GFP_KERNEL);
- if (!fi->fib_metrics)
- goto failure;
+ if (unlikely(!fi->fib_metrics)) {
+ kfree(fi);
+ return ERR_PTR(err);
+ }
atomic_set(&fi->fib_metrics->refcnt, 1);
- } else
+ } else {
fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics;
-
+ }
+ fib_info_cnt++;
fi->fib_net = net;
fi->fib_protocol = cfg->fc_protocol;
fi->fib_scope = cfg->fc_scope;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 661bda968594..62e41d38da78 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -922,10 +922,12 @@ static int __ip_append_data(struct sock *sk,
csummode = CHECKSUM_PARTIAL;
cork->length += length;
- if (((length > mtu) || (skb && skb_is_gso(skb))) &&
+ if ((skb && skb_is_gso(skb)) ||
+ (((length + (skb ? skb->len : fragheaderlen)) > mtu) &&
+ (skb_queue_len(queue) <= 1) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len &&
- (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx) {
+ (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx)) {
err = ip_ufo_append_data(sk, queue, getfrag, from, length,
hh_len, fragheaderlen, transhdrlen,
maxfraglen, flags);
@@ -1241,6 +1243,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
return -EINVAL;
if ((size + skb->len > mtu) &&
+ (skb_queue_len(&sk->sk_write_queue) == 1) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO)) {
if (skb->ip_summed != CHECKSUM_PARTIAL)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index fd15e55b28d1..5bdc0caa7f4c 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1247,7 +1247,7 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst)
if (mtu)
return mtu;
- mtu = dst->dev->mtu;
+ mtu = READ_ONCE(dst->dev->mtu);
if (unlikely(dst_metric_locked(dst, RTAX_MTU))) {
if (rt->rt_uses_gateway && mtu > 576)
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 2dc982b15df8..a2e1142145df 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -337,6 +337,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
treq = tcp_rsk(req);
treq->rcv_isn = ntohl(th->seq) - 1;
treq->snt_isn = cookie;
+ treq->txhash = net_tx_rndhash();
req->mss = mss;
ireq->ir_num = ntohs(th->dest);
ireq->ir_rmt_port = th->source;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 57e5938fd669..9bdd7847ef3a 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2272,6 +2272,8 @@ int tcp_disconnect(struct sock *sk, int flags)
tcp_init_send_head(sk);
memset(&tp->rx_opt, 0, sizeof(tp->rx_opt));
__sk_dst_reset(sk);
+ dst_release(sk->sk_rx_dst);
+ sk->sk_rx_dst = NULL;
tcp_saved_syn_free(tp);
WARN_ON(inet->inet_num && !icsk->icsk_bind_hash);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 710101376a76..0047b151e8e8 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2166,8 +2166,7 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
- int cnt, oldcnt;
- int err;
+ int cnt, oldcnt, lost;
unsigned int mss;
/* Use SACK to deduce losses of new sequences sent during recovery */
const u32 loss_high = tcp_is_sack(tp) ? tp->snd_nxt : tp->high_seq;
@@ -2207,9 +2206,10 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head)
break;
mss = tcp_skb_mss(skb);
- err = tcp_fragment(sk, skb, (packets - oldcnt) * mss,
- mss, GFP_ATOMIC);
- if (err < 0)
+ /* If needed, chop off the prefix to mark as lost. */
+ lost = (packets - oldcnt) * mss;
+ if (lost < skb->len &&
+ tcp_fragment(sk, skb, lost, mss, GFP_ATOMIC) < 0)
break;
cnt = packets;
}
@@ -2504,8 +2504,8 @@ static inline void tcp_end_cwnd_reduction(struct sock *sk)
struct tcp_sock *tp = tcp_sk(sk);
/* Reset cwnd to ssthresh in CWR or Recovery (unless it's undone) */
- if (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR ||
- (tp->undo_marker && tp->snd_ssthresh < TCP_INFINITE_SSTHRESH)) {
+ if (tp->snd_ssthresh < TCP_INFINITE_SSTHRESH &&
+ (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR || tp->undo_marker)) {
tp->snd_cwnd = tp->snd_ssthresh;
tp->snd_cwnd_stamp = tcp_time_stamp;
}
@@ -3029,8 +3029,7 @@ void tcp_rearm_rto(struct sock *sk)
/* delta may not be positive if the socket is locked
* when the retrans timer fires and is rescheduled.
*/
- if (delta > 0)
- rto = delta;
+ rto = max(delta, 1);
}
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, rto,
TCP_RTO_MAX);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 2ca323b68efd..4e88f93f71c8 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3256,6 +3256,9 @@ int tcp_connect(struct sock *sk)
struct sk_buff *buff;
int err;
+ if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk))
+ return -EHOSTUNREACH; /* Routing failure or similar. */
+
tcp_connect_init(sk);
if (unlikely(tp->repair)) {
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 4aef80d30fab..c1a84472cc0c 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -640,7 +640,8 @@ static void tcp_keepalive_timer (unsigned long data)
goto death;
}
- if (!sock_flag(sk, SOCK_KEEPOPEN) || sk->sk_state == TCP_CLOSE)
+ if (!sock_flag(sk, SOCK_KEEPOPEN) ||
+ ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)))
goto out;
elapsed = keepalive_time_when(tp);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 254fcc7f1825..4d6f09c05a12 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -824,7 +824,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
if (is_udplite) /* UDP-Lite */
csum = udplite_csum(skb);
- else if (sk->sk_no_check_tx) { /* UDP csum disabled */
+ else if (sk->sk_no_check_tx && !skb_is_gso(skb)) { /* UDP csum off */
skb->ip_summed = CHECKSUM_NONE;
goto send;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 6396f1c80ae9..6dfc3daf7c21 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -231,7 +231,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
- skb->ip_summed = CHECKSUM_NONE;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
/* Fragment the skb. IP headers of the fragments are updated in
* inet_gso_segment()
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 55e928819846..4b707ad4ffbd 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1801,17 +1801,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
{
- if (ifp->flags&IFA_F_PERMANENT) {
- spin_lock_bh(&ifp->lock);
- addrconf_del_dad_work(ifp);
- ifp->flags |= IFA_F_TENTATIVE;
- if (dad_failed)
- ifp->flags |= IFA_F_DADFAILED;
- spin_unlock_bh(&ifp->lock);
- if (dad_failed)
- ipv6_ifa_notify(0, ifp);
- in6_ifa_put(ifp);
- } else if (ifp->flags&IFA_F_TEMPORARY) {
+ if (ifp->flags&IFA_F_TEMPORARY) {
struct inet6_ifaddr *ifpub;
spin_lock_bh(&ifp->lock);
ifpub = ifp->ifpub;
@@ -1824,6 +1814,16 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
spin_unlock_bh(&ifp->lock);
}
ipv6_del_addr(ifp);
+ } else if (ifp->flags&IFA_F_PERMANENT || !dad_failed) {
+ spin_lock_bh(&ifp->lock);
+ addrconf_del_dad_work(ifp);
+ ifp->flags |= IFA_F_TENTATIVE;
+ if (dad_failed)
+ ifp->flags |= IFA_F_DADFAILED;
+ spin_unlock_bh(&ifp->lock);
+ if (dad_failed)
+ ipv6_ifa_notify(0, ifp);
+ in6_ifa_put(ifp);
} else {
ipv6_del_addr(ifp);
}
@@ -3212,6 +3212,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct inet6_dev *idev = __in6_dev_get(dev);
+ struct net *net = dev_net(dev);
int run_pending = 0;
int err;
@@ -3227,7 +3228,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
case NETDEV_CHANGEMTU:
/* if MTU under IPV6_MIN_MTU stop IPv6 on this interface. */
if (dev->mtu < IPV6_MIN_MTU) {
- addrconf_ifdown(dev, 1);
+ addrconf_ifdown(dev, dev != net->loopback_dev);
break;
}
@@ -3340,7 +3341,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
* IPV6_MIN_MTU stop IPv6 on this interface.
*/
if (dev->mtu < IPV6_MIN_MTU)
- addrconf_ifdown(dev, 1);
+ addrconf_ifdown(dev, dev != net->loopback_dev);
}
break;
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 1ac06723f0d7..aad8cdf15472 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -767,10 +767,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
goto next_iter;
}
- if (iter->dst.dev == rt->dst.dev &&
- iter->rt6i_idev == rt->rt6i_idev &&
- ipv6_addr_equal(&iter->rt6i_gateway,
- &rt->rt6i_gateway)) {
+ if (rt6_duplicate_nexthop(iter, rt)) {
if (rt->rt6i_nsiblings)
rt->rt6i_nsiblings = 0;
if (!(iter->rt6i_flags & RTF_EXPIRES))
@@ -895,6 +892,8 @@ add:
}
nsiblings = iter->rt6i_nsiblings;
fib6_purge_rt(iter, fn, info->nl_net);
+ if (fn->rr_ptr == iter)
+ fn->rr_ptr = NULL;
rt6_release(iter);
if (nsiblings) {
@@ -907,6 +906,8 @@ add:
if (rt6_qualify_for_ecmp(iter)) {
*ins = iter->dst.rt6_next;
fib6_purge_rt(iter, fn, info->nl_net);
+ if (fn->rr_ptr == iter)
+ fn->rr_ptr = NULL;
rt6_release(iter);
nsiblings--;
} else {
@@ -995,7 +996,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
/* Create subtree root node */
sfn = node_alloc();
if (!sfn)
- goto st_failure;
+ goto failure;
sfn->leaf = info->nl_net->ipv6.ip6_null_entry;
atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref);
@@ -1011,12 +1012,12 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
if (IS_ERR(sn)) {
/* If it is failed, discard just allocated
- root, and then (in st_failure) stale node
+ root, and then (in failure) stale node
in main tree.
*/
node_free(sfn);
err = PTR_ERR(sn);
- goto st_failure;
+ goto failure;
}
/* Now link new subtree to main tree */
@@ -1030,7 +1031,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
if (IS_ERR(sn)) {
err = PTR_ERR(sn);
- goto st_failure;
+ goto failure;
}
}
@@ -1072,22 +1073,22 @@ out:
atomic_inc(&pn->leaf->rt6i_ref);
}
#endif
- if (!(rt->dst.flags & DST_NOCACHE))
- dst_free(&rt->dst);
+ goto failure;
}
return err;
-#ifdef CONFIG_IPV6_SUBTREES
- /* Subtree creation failed, probably main tree node
- is orphan. If it is, shoot it.
+failure:
+ /* fn->leaf could be NULL if fn is an intermediate node and we
+ * failed to add the new route to it in both subtree creation
+ * failure and fib6_add_rt2node() failure case.
+ * In both cases, fib6_repair_tree() should be called to fix
+ * fn->leaf.
*/
-st_failure:
if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)))
fib6_repair_tree(info->nl_net, fn);
if (!(rt->dst.flags & DST_NOCACHE))
dst_free(&rt->dst);
return err;
-#endif
}
/*
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 72b53b83a720..635742a6d73f 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -647,8 +647,6 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
*prevhdr = NEXTHDR_FRAGMENT;
tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);
if (!tmp_hdr) {
- IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
- IPSTATS_MIB_FRAGFAILS);
err = -ENOMEM;
goto fail;
}
@@ -767,8 +765,6 @@ slow_path:
frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) +
hroom + troom, GFP_ATOMIC);
if (!frag) {
- IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
- IPSTATS_MIB_FRAGFAILS);
err = -ENOMEM;
goto fail;
}
@@ -1360,11 +1356,12 @@ emsgsize:
*/
cork->length += length;
- if (((length > mtu) ||
- (skb && skb_is_gso(skb))) &&
+ if ((skb && skb_is_gso(skb)) ||
+ (((length + (skb ? skb->len : headersize)) > mtu) &&
+ (skb_queue_len(queue) <= 1) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO) &&
- (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) {
+ (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk))) {
err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
hh_len, fragheaderlen, exthdrlen,
transhdrlen, mtu, flags, fl6);
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
index 8b56c5240429..f9f02581c4ca 100644
--- a/net/ipv6/output_core.c
+++ b/net/ipv6/output_core.c
@@ -78,7 +78,7 @@ EXPORT_SYMBOL(ipv6_select_ident);
int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
{
- u16 offset = sizeof(struct ipv6hdr);
+ unsigned int offset = sizeof(struct ipv6hdr);
unsigned int packet_len = skb_tail_pointer(skb) -
skb_network_header(skb);
int found_rhdr = 0;
@@ -86,6 +86,7 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
while (offset <= packet_len) {
struct ipv6_opt_hdr *exthdr;
+ unsigned int len;
switch (**nexthdr) {
@@ -111,7 +112,10 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
offset);
- offset += ipv6_optlen(exthdr);
+ len = ipv6_optlen(exthdr);
+ if (len + offset >= IPV6_MAXPLEN)
+ return -EINVAL;
+ offset += len;
*nexthdr = &exthdr->nexthdr;
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index e98613d2f34f..dd37fe0b6a49 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2822,17 +2822,11 @@ static int ip6_route_info_append(struct list_head *rt6_nh_list,
struct rt6_info *rt, struct fib6_config *r_cfg)
{
struct rt6_nh *nh;
- struct rt6_info *rtnh;
int err = -EEXIST;
list_for_each_entry(nh, rt6_nh_list, next) {
/* check if rt6_info already exists */
- rtnh = nh->rt6_info;
-
- if (rtnh->dst.dev == rt->dst.dev &&
- rtnh->rt6i_idev == rt->rt6i_idev &&
- ipv6_addr_equal(&rtnh->rt6i_gateway,
- &rt->rt6i_gateway))
+ if (rt6_duplicate_nexthop(nh->rt6_info, rt))
return err;
}
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 336843ca4e6b..7f3667635431 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -210,6 +210,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
treq->snt_synack.v64 = 0;
treq->rcv_isn = ntohl(th->seq) - 1;
treq->snt_isn = cookie;
+ treq->txhash = net_tx_rndhash();
/*
* We need to lookup the dst_entry to get the correct window size.
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 01582966ffa0..2e3c12eeca07 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -86,7 +86,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
- skb->ip_summed = CHECKSUM_NONE;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
/* Check if there is enough headroom to insert fragment header. */
tnl_hlen = skb_tnl_header_len(skb);
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index 8d2f7c9b491d..4a116d766c15 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -2227,7 +2227,7 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
{
struct sock *sk = sock->sk;
struct irda_sock *self = irda_sk(sk);
- struct irda_device_list list;
+ struct irda_device_list list = { 0 };
struct irda_device_info *discoveries;
struct irda_ias_set * ias_opt; /* IAS get/query params */
struct ias_object * ias_obj; /* Object in IAS */
diff --git a/net/key/af_key.c b/net/key/af_key.c
index e67c28e614b9..94bf810ad242 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -63,8 +63,13 @@ struct pfkey_sock {
} u;
struct sk_buff *skb;
} dump;
+ struct mutex dump_lock;
};
+static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
+ xfrm_address_t *saddr, xfrm_address_t *daddr,
+ u16 *family);
+
static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
{
return (struct pfkey_sock *)sk;
@@ -139,6 +144,7 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol,
{
struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
struct sock *sk;
+ struct pfkey_sock *pfk;
int err;
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
@@ -153,6 +159,9 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol,
if (sk == NULL)
goto out;
+ pfk = pfkey_sk(sk);
+ mutex_init(&pfk->dump_lock);
+
sock->ops = &pfkey_ops;
sock_init_data(sock, sk);
@@ -219,7 +228,7 @@ static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,
#define BROADCAST_ONE 1
#define BROADCAST_REGISTERED 2
#define BROADCAST_PROMISC_ONLY 4
-static int pfkey_broadcast(struct sk_buff *skb,
+static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
int broadcast_flags, struct sock *one_sk,
struct net *net)
{
@@ -269,7 +278,7 @@ static int pfkey_broadcast(struct sk_buff *skb,
rcu_read_unlock();
if (one_sk != NULL)
- err = pfkey_broadcast_one(skb, &skb2, GFP_KERNEL, one_sk);
+ err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk);
kfree_skb(skb2);
kfree_skb(skb);
@@ -281,23 +290,36 @@ static int pfkey_do_dump(struct pfkey_sock *pfk)
struct sadb_msg *hdr;
int rc;
+ mutex_lock(&pfk->dump_lock);
+ if (!pfk->dump.dump) {
+ rc = 0;
+ goto out;
+ }
+
rc = pfk->dump.dump(pfk);
- if (rc == -ENOBUFS)
- return 0;
+ if (rc == -ENOBUFS) {
+ rc = 0;
+ goto out;
+ }
if (pfk->dump.skb) {
- if (!pfkey_can_dump(&pfk->sk))
- return 0;
+ if (!pfkey_can_dump(&pfk->sk)) {
+ rc = 0;
+ goto out;
+ }
hdr = (struct sadb_msg *) pfk->dump.skb->data;
hdr->sadb_msg_seq = 0;
hdr->sadb_msg_errno = rc;
- pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE,
+ pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
&pfk->sk, sock_net(&pfk->sk));
pfk->dump.skb = NULL;
}
pfkey_terminate_dump(pfk);
+
+out:
+ mutex_unlock(&pfk->dump_lock);
return rc;
}
@@ -333,7 +355,7 @@ static int pfkey_error(const struct sadb_msg *orig, int err, struct sock *sk)
hdr->sadb_msg_len = (sizeof(struct sadb_msg) /
sizeof(uint64_t));
- pfkey_broadcast(skb, BROADCAST_ONE, sk, sock_net(sk));
+ pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk, sock_net(sk));
return 0;
}
@@ -1374,7 +1396,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_
xfrm_state_put(x);
- pfkey_broadcast(resp_skb, BROADCAST_ONE, sk, net);
+ pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk, net);
return 0;
}
@@ -1461,7 +1483,7 @@ static int key_notify_sa(struct xfrm_state *x, const struct km_event *c)
hdr->sadb_msg_seq = c->seq;
hdr->sadb_msg_pid = c->portid;
- pfkey_broadcast(skb, BROADCAST_ALL, NULL, xs_net(x));
+ pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x));
return 0;
}
@@ -1574,7 +1596,7 @@ static int pfkey_get(struct sock *sk, struct sk_buff *skb, const struct sadb_msg
out_hdr->sadb_msg_reserved = 0;
out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
- pfkey_broadcast(out_skb, BROADCAST_ONE, sk, sock_net(sk));
+ pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk));
return 0;
}
@@ -1679,8 +1701,8 @@ static int pfkey_register(struct sock *sk, struct sk_buff *skb, const struct sad
return -ENOBUFS;
}
- pfkey_broadcast(supp_skb, BROADCAST_REGISTERED, sk, sock_net(sk));
-
+ pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk,
+ sock_net(sk));
return 0;
}
@@ -1698,7 +1720,8 @@ static int unicast_flush_resp(struct sock *sk, const struct sadb_msg *ihdr)
hdr->sadb_msg_errno = (uint8_t) 0;
hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
- return pfkey_broadcast(skb, BROADCAST_ONE, sk, sock_net(sk));
+ return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ONE, sk,
+ sock_net(sk));
}
static int key_notify_sa_flush(const struct km_event *c)
@@ -1719,7 +1742,7 @@ static int key_notify_sa_flush(const struct km_event *c)
hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
hdr->sadb_msg_reserved = 0;
- pfkey_broadcast(skb, BROADCAST_ALL, NULL, c->net);
+ pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
return 0;
}
@@ -1776,7 +1799,7 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr)
out_hdr->sadb_msg_pid = pfk->dump.msg_portid;
if (pfk->dump.skb)
- pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE,
+ pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
&pfk->sk, sock_net(&pfk->sk));
pfk->dump.skb = out_skb;
@@ -1802,19 +1825,26 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms
struct xfrm_address_filter *filter = NULL;
struct pfkey_sock *pfk = pfkey_sk(sk);
- if (pfk->dump.dump != NULL)
+ mutex_lock(&pfk->dump_lock);
+ if (pfk->dump.dump != NULL) {
+ mutex_unlock(&pfk->dump_lock);
return -EBUSY;
+ }
proto = pfkey_satype2proto(hdr->sadb_msg_satype);
- if (proto == 0)
+ if (proto == 0) {
+ mutex_unlock(&pfk->dump_lock);
return -EINVAL;
+ }
if (ext_hdrs[SADB_X_EXT_FILTER - 1]) {
struct sadb_x_filter *xfilter = ext_hdrs[SADB_X_EXT_FILTER - 1];
filter = kmalloc(sizeof(*filter), GFP_KERNEL);
- if (filter == NULL)
+ if (filter == NULL) {
+ mutex_unlock(&pfk->dump_lock);
return -ENOMEM;
+ }
memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr,
sizeof(xfrm_address_t));
@@ -1830,6 +1860,7 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms
pfk->dump.dump = pfkey_dump_sa;
pfk->dump.done = pfkey_dump_sa_done;
xfrm_state_walk_init(&pfk->dump.u.state, proto, filter);
+ mutex_unlock(&pfk->dump_lock);
return pfkey_do_dump(pfk);
}
@@ -1856,7 +1887,7 @@ static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, const struct sadb
new_hdr->sadb_msg_errno = 0;
}
- pfkey_broadcast(skb, BROADCAST_ALL, NULL, sock_net(sk));
+ pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ALL, NULL, sock_net(sk));
return 0;
}
@@ -1922,19 +1953,14 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
/* addresses present only in tunnel mode */
if (t->mode == XFRM_MODE_TUNNEL) {
- u8 *sa = (u8 *) (rq + 1);
- int family, socklen;
-
- family = pfkey_sockaddr_extract((struct sockaddr *)sa,
- &t->saddr);
- if (!family)
- return -EINVAL;
+ int err;
- socklen = pfkey_sockaddr_len(family);
- if (pfkey_sockaddr_extract((struct sockaddr *)(sa + socklen),
- &t->id.daddr) != family)
- return -EINVAL;
- t->encap_family = family;
+ err = parse_sockaddr_pair(
+ (struct sockaddr *)(rq + 1),
+ rq->sadb_x_ipsecrequest_len - sizeof(*rq),
+ &t->saddr, &t->id.daddr, &t->encap_family);
+ if (err)
+ return err;
} else
t->encap_family = xp->family;
@@ -1954,7 +1980,11 @@ parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
if (pol->sadb_x_policy_len * 8 < sizeof(struct sadb_x_policy))
return -EINVAL;
- while (len >= sizeof(struct sadb_x_ipsecrequest)) {
+ while (len >= sizeof(*rq)) {
+ if (len < rq->sadb_x_ipsecrequest_len ||
+ rq->sadb_x_ipsecrequest_len < sizeof(*rq))
+ return -EINVAL;
+
if ((err = parse_ipsecrequest(xp, rq)) < 0)
return err;
len -= rq->sadb_x_ipsecrequest_len;
@@ -2190,7 +2220,7 @@ static int key_notify_policy(struct xfrm_policy *xp, int dir, const struct km_ev
out_hdr->sadb_msg_errno = 0;
out_hdr->sadb_msg_seq = c->seq;
out_hdr->sadb_msg_pid = c->portid;
- pfkey_broadcast(out_skb, BROADCAST_ALL, NULL, xp_net(xp));
+ pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xp_net(xp));
return 0;
}
@@ -2410,14 +2440,13 @@ static int key_pol_get_resp(struct sock *sk, struct xfrm_policy *xp, const struc
out_hdr->sadb_msg_errno = 0;
out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
- pfkey_broadcast(out_skb, BROADCAST_ONE, sk, xp_net(xp));
+ pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, xp_net(xp));
err = 0;
out:
return err;
}
-#ifdef CONFIG_NET_KEY_MIGRATE
static int pfkey_sockaddr_pair_size(sa_family_t family)
{
return PFKEY_ALIGN8(pfkey_sockaddr_len(family) * 2);
@@ -2429,7 +2458,7 @@ static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
{
int af, socklen;
- if (ext_len < pfkey_sockaddr_pair_size(sa->sa_family))
+ if (ext_len < 2 || ext_len < pfkey_sockaddr_pair_size(sa->sa_family))
return -EINVAL;
af = pfkey_sockaddr_extract(sa, saddr);
@@ -2445,6 +2474,7 @@ static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
return 0;
}
+#ifdef CONFIG_NET_KEY_MIGRATE
static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
struct xfrm_migrate *m)
{
@@ -2452,13 +2482,14 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
struct sadb_x_ipsecrequest *rq2;
int mode;
- if (len <= sizeof(struct sadb_x_ipsecrequest) ||
- len < rq1->sadb_x_ipsecrequest_len)
+ if (len < sizeof(*rq1) ||
+ len < rq1->sadb_x_ipsecrequest_len ||
+ rq1->sadb_x_ipsecrequest_len < sizeof(*rq1))
return -EINVAL;
/* old endoints */
err = parse_sockaddr_pair((struct sockaddr *)(rq1 + 1),
- rq1->sadb_x_ipsecrequest_len,
+ rq1->sadb_x_ipsecrequest_len - sizeof(*rq1),
&m->old_saddr, &m->old_daddr,
&m->old_family);
if (err)
@@ -2467,13 +2498,14 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
rq2 = (struct sadb_x_ipsecrequest *)((u8 *)rq1 + rq1->sadb_x_ipsecrequest_len);
len -= rq1->sadb_x_ipsecrequest_len;
- if (len <= sizeof(struct sadb_x_ipsecrequest) ||
- len < rq2->sadb_x_ipsecrequest_len)
+ if (len <= sizeof(*rq2) ||
+ len < rq2->sadb_x_ipsecrequest_len ||
+ rq2->sadb_x_ipsecrequest_len < sizeof(*rq2))
return -EINVAL;
/* new endpoints */
err = parse_sockaddr_pair((struct sockaddr *)(rq2 + 1),
- rq2->sadb_x_ipsecrequest_len,
+ rq2->sadb_x_ipsecrequest_len - sizeof(*rq2),
&m->new_saddr, &m->new_daddr,
&m->new_family);
if (err)
@@ -2664,7 +2696,7 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
out_hdr->sadb_msg_pid = pfk->dump.msg_portid;
if (pfk->dump.skb)
- pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE,
+ pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
&pfk->sk, sock_net(&pfk->sk));
pfk->dump.skb = out_skb;
@@ -2688,14 +2720,18 @@ static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, const struct sadb
{
struct pfkey_sock *pfk = pfkey_sk(sk);
- if (pfk->dump.dump != NULL)
+ mutex_lock(&pfk->dump_lock);
+ if (pfk->dump.dump != NULL) {
+ mutex_unlock(&pfk->dump_lock);
return -EBUSY;
+ }
pfk->dump.msg_version = hdr->sadb_msg_version;
pfk->dump.msg_portid = hdr->sadb_msg_pid;
pfk->dump.dump = pfkey_dump_sp;
pfk->dump.done = pfkey_dump_sp_done;
xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN);
+ mutex_unlock(&pfk->dump_lock);
return pfkey_do_dump(pfk);
}
@@ -2717,7 +2753,7 @@ static int key_notify_policy_flush(const struct km_event *c)
hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
hdr->sadb_msg_reserved = 0;
- pfkey_broadcast(skb_out, BROADCAST_ALL, NULL, c->net);
+ pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
return 0;
}
@@ -2779,7 +2815,7 @@ static int pfkey_process(struct sock *sk, struct sk_buff *skb, const struct sadb
void *ext_hdrs[SADB_EXT_MAX];
int err;
- pfkey_broadcast(skb_clone(skb, GFP_KERNEL),
+ pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL,
BROADCAST_PROMISC_ONLY, NULL, sock_net(sk));
memset(ext_hdrs, 0, sizeof(ext_hdrs));
@@ -3001,7 +3037,8 @@ static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c)
out_hdr->sadb_msg_seq = 0;
out_hdr->sadb_msg_pid = 0;
- pfkey_broadcast(out_skb, BROADCAST_REGISTERED, NULL, xs_net(x));
+ pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL,
+ xs_net(x));
return 0;
}
@@ -3191,7 +3228,8 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
xfrm_ctx->ctx_len);
}
- return pfkey_broadcast(skb, BROADCAST_REGISTERED, NULL, xs_net(x));
+ return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL,
+ xs_net(x));
}
static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
@@ -3389,7 +3427,8 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
n_port->sadb_x_nat_t_port_port = sport;
n_port->sadb_x_nat_t_port_reserved = 0;
- return pfkey_broadcast(skb, BROADCAST_REGISTERED, NULL, xs_net(x));
+ return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL,
+ xs_net(x));
}
#ifdef CONFIG_NET_KEY_MIGRATE
@@ -3581,7 +3620,7 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
}
/* broadcast migrate message to sockets */
- pfkey_broadcast(skb, BROADCAST_ALL, NULL, &init_net);
+ pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net);
return 0;
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 4da560005b0e..dd1649caa2b2 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -845,10 +845,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
{
unsigned int verdict = NF_DROP;
- if (IP_VS_FWD_METHOD(cp) != 0) {
- pr_err("shouldn't reach here, because the box is on the "
- "half connection in the tun/dr module.\n");
- }
+ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+ goto ignore_cp;
/* Ensure the checksum is correct */
if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
@@ -882,6 +880,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
ip_vs_notrack(skb);
else
ip_vs_update_conntrack(skb, cp, 0);
+
+ignore_cp:
verdict = NF_ACCEPT;
out:
@@ -1242,8 +1242,11 @@ ip_vs_out(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, in
*/
cp = pp->conn_out_get(ipvs, af, skb, &iph);
- if (likely(cp))
+ if (likely(cp)) {
+ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+ goto ignore_cp;
return handle_response(af, skb, pd, cp, &iph, hooknum);
+ }
if (sysctl_nat_icmp_send(ipvs) &&
(pp->protocol == IPPROTO_TCP ||
pp->protocol == IPPROTO_UDP ||
@@ -1285,9 +1288,15 @@ ip_vs_out(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, in
}
}
}
+
+out:
IP_VS_DBG_PKT(12, af, pp, skb, iph.off,
"ip_vs_out: packet continues traversal as normal");
return NF_ACCEPT;
+
+ignore_cp:
+ __ip_vs_conn_put(cp);
+ goto out;
}
/*
diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c
index 1a9545965c0d..531ca55f1af6 100644
--- a/net/netfilter/nf_conntrack_extend.c
+++ b/net/netfilter/nf_conntrack_extend.c
@@ -53,7 +53,11 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id,
rcu_read_lock();
t = rcu_dereference(nf_ct_ext_types[id]);
- BUG_ON(t == NULL);
+ if (!t) {
+ rcu_read_unlock();
+ return NULL;
+ }
+
off = ALIGN(sizeof(struct nf_ct_ext), t->align);
len = off + t->len + var_alloc_len;
alloc_size = t->alloc_size + var_alloc_len;
@@ -88,7 +92,10 @@ void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id,
rcu_read_lock();
t = rcu_dereference(nf_ct_ext_types[id]);
- BUG_ON(t == NULL);
+ if (!t) {
+ rcu_read_unlock();
+ return NULL;
+ }
newoff = ALIGN(old->len, t->align);
newlen = newoff + t->len + var_alloc_len;
@@ -186,6 +193,6 @@ void nf_ct_extend_unregister(struct nf_ct_ext_type *type)
RCU_INIT_POINTER(nf_ct_ext_types[type->id], NULL);
update_alloc_size(type);
mutex_unlock(&nf_ct_ext_type_mutex);
- rcu_barrier(); /* Wait for completion of call_rcu()'s */
+ synchronize_rcu();
}
EXPORT_SYMBOL_GPL(nf_ct_extend_unregister);
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index fef69cb21f6f..b0e0555e79ad 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -1713,18 +1713,9 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
}
MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n",
par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par));
- if (sk != NULL) {
- set_sk_callback_lock = true;
- read_lock_bh(&sk->sk_callback_lock);
- MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n",
- par->hooknum, sk, sk->sk_socket,
- sk->sk_socket ? sk->sk_socket->file : (void *)-1LL);
- filp = sk->sk_socket ? sk->sk_socket->file : NULL;
- MT_DEBUG("qtaguid[%d]: filp...uid=%u\n",
- par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1);
- }
- if (sk == NULL || sk->sk_socket == NULL) {
+
+ if (sk == NULL) {
/*
* Here, the qtaguid_find_sk() using connection tracking
* couldn't find the owner, so for now we just count them
@@ -1732,9 +1723,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
*/
if (do_tag_stat)
account_for_uid(skb, sk, 0, par);
- MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n",
- par->hooknum,
- sk ? sk->sk_socket : NULL);
+ MT_DEBUG("qtaguid[%d]: leaving (sk=NULL)\n", par->hooknum);
res = (info->match ^ info->invert) == 0;
atomic64_inc(&qtu_events.match_no_sk);
goto put_sock_ret_res;
@@ -1742,17 +1731,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
res = false;
goto put_sock_ret_res;
}
- filp = sk->sk_socket->file;
- if (filp == NULL) {
- MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum);
- if (do_tag_stat)
- account_for_uid(skb, sk, 0, par);
- res = ((info->match ^ info->invert) &
- (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0;
- atomic64_inc(&qtu_events.match_no_sk_file);
- goto put_sock_ret_res;
- }
- sock_uid = filp->f_cred->fsuid;
+ sock_uid = sk->sk_uid;
if (do_tag_stat)
account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par);
@@ -1766,8 +1745,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min);
kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max);
- if ((uid_gte(filp->f_cred->fsuid, uid_min) &&
- uid_lte(filp->f_cred->fsuid, uid_max)) ^
+ if ((uid_gte(sk->sk_uid, uid_min) &&
+ uid_lte(sk->sk_uid, uid_max)) ^
!(info->invert & XT_QTAGUID_UID)) {
MT_DEBUG("qtaguid[%d]: leaving uid not matching\n",
par->hooknum);
@@ -1778,7 +1757,19 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
if (info->match & XT_QTAGUID_GID) {
kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min);
kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max);
-
+ set_sk_callback_lock = true;
+ read_lock_bh(&sk->sk_callback_lock);
+ MT_DEBUG("qtaguid[%d]: sk=%pK->sk_socket=%pK->file=%pK\n",
+ par->hooknum, sk, sk->sk_socket,
+ sk->sk_socket ? sk->sk_socket->file : (void *)-1LL);
+ filp = sk->sk_socket ? sk->sk_socket->file : NULL;
+ if (!filp) {
+ res = ((info->match ^ info->invert) & XT_QTAGUID_GID) == 0;
+ atomic64_inc(&qtu_events.match_no_sk_gid);
+ goto put_sock_ret_res;
+ }
+ MT_DEBUG("qtaguid[%d]: filp...uid=%u\n",
+ par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1);
if ((gid_gte(filp->f_cred->fsgid, gid_min) &&
gid_lte(filp->f_cred->fsgid, gid_max)) ^
!(info->invert & XT_QTAGUID_GID)) {
@@ -1950,7 +1941,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
"match_found_sk_in_ct=%llu "
"match_found_no_sk_in_ct=%llu "
"match_no_sk=%llu "
- "match_no_sk_file=%llu\n",
+ "match_no_sk_gid=%llu\n",
(u64)atomic64_read(&qtu_events.sockets_tagged),
(u64)atomic64_read(&qtu_events.sockets_untagged),
(u64)atomic64_read(&qtu_events.counter_set_changes),
@@ -1962,7 +1953,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
(u64)atomic64_read(&qtu_events.match_found_sk_in_ct),
(u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct),
(u64)atomic64_read(&qtu_events.match_no_sk),
- (u64)atomic64_read(&qtu_events.match_no_sk_file));
+ (u64)atomic64_read(&qtu_events.match_no_sk_gid));
/* Count the following as part of the last item_index. No need
* to lock the sock_tag_list here since it is already locked when
diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h
index 8178fbdfb036..c7052707a6a4 100644
--- a/net/netfilter/xt_qtaguid_internal.h
+++ b/net/netfilter/xt_qtaguid_internal.h
@@ -289,10 +289,10 @@ struct qtaguid_event_counts {
*/
atomic64_t match_no_sk;
/*
- * The file ptr in the sk_socket wasn't there.
+ * The file ptr in the sk_socket wasn't there and we couldn't get GID.
* This might happen for traffic while the socket is being closed.
*/
- atomic64_t match_no_sk_file;
+ atomic64_t match_no_sk_gid;
};
/* Track the set active_set for the given tag. */
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 1fe3d3b362c0..c5a2c7e733b3 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -969,6 +969,8 @@ static void nfc_release(struct device *d)
kfree(se);
}
+ ida_simple_remove(&nfc_index_ida, dev->idx);
+
kfree(dev);
}
@@ -1043,6 +1045,7 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
int tx_headroom, int tx_tailroom)
{
struct nfc_dev *dev;
+ int rc;
if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
!ops->deactivate_target || !ops->im_transceive)
@@ -1055,6 +1058,15 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
if (!dev)
return NULL;
+ rc = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL);
+ if (rc < 0)
+ goto err_free_dev;
+ dev->idx = rc;
+
+ dev->dev.class = &nfc_class;
+ dev_set_name(&dev->dev, "nfc%d", dev->idx);
+ device_initialize(&dev->dev);
+
dev->ops = ops;
dev->supported_protocols = supported_protocols;
dev->tx_headroom = tx_headroom;
@@ -1077,6 +1089,11 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
}
return dev;
+
+err_free_dev:
+ kfree(dev);
+
+ return ERR_PTR(rc);
}
EXPORT_SYMBOL(nfc_allocate_device);
@@ -1091,14 +1108,6 @@ int nfc_register_device(struct nfc_dev *dev)
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
- dev->idx = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL);
- if (dev->idx < 0)
- return dev->idx;
-
- dev->dev.class = &nfc_class;
- dev_set_name(&dev->dev, "nfc%d", dev->idx);
- device_initialize(&dev->dev);
-
mutex_lock(&nfc_devlist_mutex);
nfc_devlist_generation++;
rc = device_add(&dev->dev);
@@ -1136,12 +1145,10 @@ EXPORT_SYMBOL(nfc_register_device);
*/
void nfc_unregister_device(struct nfc_dev *dev)
{
- int rc, id;
+ int rc;
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
- id = dev->idx;
-
if (dev->rfkill) {
rfkill_unregister(dev->rfkill);
rfkill_destroy(dev->rfkill);
@@ -1166,8 +1173,6 @@ void nfc_unregister_device(struct nfc_dev *dev)
nfc_devlist_generation++;
device_del(&dev->dev);
mutex_unlock(&nfc_devlist_mutex);
-
- ida_simple_remove(&nfc_index_ida, id);
}
EXPORT_SYMBOL(nfc_unregister_device);
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 2b0f0ac498d2..5a58f9f38095 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -209,6 +209,11 @@ void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
}
create_info = (struct hci_create_pipe_resp *)skb->data;
+ if (create_info->pipe >= NFC_HCI_MAX_PIPES) {
+ status = NFC_HCI_ANY_E_NOK;
+ goto exit;
+ }
+
/* Save the new created pipe and bind with local gate,
* the description for skb->data[3] is destination gate id
* but since we received this cmd from host controller, we
@@ -232,6 +237,11 @@ void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
}
delete_info = (struct hci_delete_pipe_noti *)skb->data;
+ if (delete_info->pipe >= NFC_HCI_MAX_PIPES) {
+ status = NFC_HCI_ANY_E_NOK;
+ goto exit;
+ }
+
hdev->pipes[delete_info->pipe].gate = NFC_HCI_INVALID_GATE;
hdev->pipes[delete_info->pipe].dest_host = NFC_HCI_INVALID_HOST;
break;
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index ecf0a0196f18..9c222a106c7f 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -76,7 +76,8 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
struct sockaddr_nfc_llcp llcp_addr;
int len, ret = 0;
- if (!addr || addr->sa_family != AF_NFC)
+ if (!addr || alen < offsetofend(struct sockaddr, sa_family) ||
+ addr->sa_family != AF_NFC)
return -EINVAL;
pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family);
@@ -150,7 +151,8 @@ static int llcp_raw_sock_bind(struct socket *sock, struct sockaddr *addr,
struct sockaddr_nfc_llcp llcp_addr;
int len, ret = 0;
- if (!addr || addr->sa_family != AF_NFC)
+ if (!addr || alen < offsetofend(struct sockaddr, sa_family) ||
+ addr->sa_family != AF_NFC)
return -EINVAL;
pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family);
@@ -655,8 +657,7 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
pr_debug("sock %p sk %p flags 0x%x\n", sock, sk, flags);
- if (!addr || len < sizeof(struct sockaddr_nfc) ||
- addr->sa_family != AF_NFC)
+ if (!addr || len < sizeof(*addr) || addr->sa_family != AF_NFC)
return -EINVAL;
if (addr->service_name_len == 0 && addr->dsap == 0)
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 10c99a578421..67583ad7f610 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -1084,8 +1084,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
return ndev;
free_nfc:
- kfree(ndev->nfc_dev);
-
+ nfc_free_device(ndev->nfc_dev);
free_nci:
kfree(ndev);
return NULL;
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index f58c1fba1026..12dfb457275d 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -873,7 +873,9 @@ static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info)
u32 device_idx, target_idx, protocol;
int rc;
- if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+ !info->attrs[NFC_ATTR_TARGET_INDEX] ||
+ !info->attrs[NFC_ATTR_PROTOCOLS])
return -EINVAL;
device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index ad58d2a6284e..6a2507f24b0f 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -577,8 +577,8 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
nla_for_each_nested(a, attr, rem) {
int type = nla_type(a);
- int maxlen = ovs_ct_attr_lens[type].maxlen;
- int minlen = ovs_ct_attr_lens[type].minlen;
+ int maxlen;
+ int minlen;
if (type > OVS_CT_ATTR_MAX) {
OVS_NLERR(log,
@@ -586,6 +586,9 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
type, OVS_CT_ATTR_MAX);
return -EINVAL;
}
+
+ maxlen = ovs_ct_attr_lens[type].maxlen;
+ minlen = ovs_ct_attr_lens[type].minlen;
if (nla_len(a) < minlen || nla_len(a) > maxlen) {
OVS_NLERR(log,
"Conntrack attr type has unexpected length (type=%d, length=%d, expected=%d)",
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index f8d6a0ca9c03..148ec130d99d 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -3622,14 +3622,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
if (optlen != sizeof(val))
return -EINVAL;
- if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)
- return -EBUSY;
if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
if (val > INT_MAX)
return -EINVAL;
- po->tp_reserve = val;
- return 0;
+ lock_sock(sk);
+ if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) {
+ ret = -EBUSY;
+ } else {
+ po->tp_reserve = val;
+ ret = 0;
+ }
+ release_sock(sk);
+ return ret;
}
case PACKET_LOSS:
{
@@ -4225,7 +4230,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
register_prot_hook(sk);
}
spin_unlock(&po->bind_lock);
- if (closing && (po->tp_version > TPACKET_V2)) {
+ if (pg_vec && (po->tp_version > TPACKET_V2)) {
/* Because we don't support block-based V3 on tx-ring */
if (!tx_ring)
prb_shutdown_retire_blk_timer(po, rb_queue);
diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c
index 0936a4a32b47..e353e3255206 100644
--- a/net/rds/tcp_listen.c
+++ b/net/rds/tcp_listen.c
@@ -78,7 +78,7 @@ int rds_tcp_accept_one(struct socket *sock)
struct inet_sock *inet;
struct rds_tcp_connection *rs_tcp;
- ret = sock_create_kern(sock_net(sock->sk), sock->sk->sk_family,
+ ret = sock_create_lite(sock->sk->sk_family,
sock->sk->sk_type, sock->sk->sk_protocol,
&new_sock);
if (ret)
diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c
index ae60f35b363d..b17556c346ce 100644
--- a/net/rmnet_data/rmnet_data_handlers.c
+++ b/net/rmnet_data/rmnet_data_handlers.c
@@ -476,10 +476,12 @@ static rx_handler_result_t _rmnet_map_ingress_handler(struct sk_buff *skb,
if (likely((ckresult == RMNET_MAP_CHECKSUM_OK)
|| (ckresult == RMNET_MAP_CHECKSUM_SKIPPED)))
skb->ip_summed |= CHECKSUM_UNNECESSARY;
- else if (ckresult != RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION
- && ckresult != RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT
- && ckresult != RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET
- && ckresult != RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET) {
+ else if (ckresult !=
+ RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION &&
+ ckresult != RMNET_MAP_CHECKSUM_VALIDATION_FAILED &&
+ ckresult != RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT &&
+ ckresult != RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET &&
+ ckresult != RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET) {
rmnet_kfree_skb(skb,
RMNET_STATS_SKBFREE_INGRESS_BAD_MAP_CKSUM);
return RX_HANDLER_CONSUMED;
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index d05869646515..075b0d22f213 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -34,6 +34,7 @@ static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int
{
struct xt_tgchk_param par;
struct xt_target *target;
+ struct ipt_entry e = {};
int ret = 0;
target = xt_request_find_target(AF_INET, t->u.user.name,
@@ -42,8 +43,9 @@ static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int
return PTR_ERR(target);
t->u.kernel.target = target;
+ memset(&par, 0, sizeof(par));
par.table = table;
- par.entryinfo = NULL;
+ par.entryinfo = &e;
par.target = target;
par.targinfo = t->data;
par.hook_mask = hook;
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 5474dc7c125a..ca4ecc246347 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1004,6 +1004,9 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
return sch;
}
+ /* ops->init() failed, we call ->destroy() like qdisc_create_dflt() */
+ if (ops->destroy)
+ ops->destroy(sch);
err_out3:
dev_put(dev);
kfree((char *) sch - sch->padded);
diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c
index 13d6f83ec491..45d4b2f22f62 100644
--- a/net/sched/sch_hhf.c
+++ b/net/sched/sch_hhf.c
@@ -636,7 +636,9 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt)
q->hhf_arrays[i] = hhf_zalloc(HHF_ARRAYS_LEN *
sizeof(u32));
if (!q->hhf_arrays[i]) {
- hhf_destroy(sch);
+ /* Note: hhf_destroy() will be called
+ * by our caller.
+ */
return -ENOMEM;
}
}
@@ -647,7 +649,9 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt)
q->hhf_valid_bits[i] = hhf_zalloc(HHF_ARRAYS_LEN /
BITS_PER_BYTE);
if (!q->hhf_valid_bits[i]) {
- hhf_destroy(sch);
+ /* Note: hhf_destroy() will be called
+ * by our caller.
+ */
return -ENOMEM;
}
}
diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c
index 3e82f047caaf..d9c84328e7eb 100644
--- a/net/sched/sch_mq.c
+++ b/net/sched/sch_mq.c
@@ -52,7 +52,7 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt)
/* pre-allocate qdiscs, attachment can't fail */
priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]),
GFP_KERNEL);
- if (priv->qdiscs == NULL)
+ if (!priv->qdiscs)
return -ENOMEM;
for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
@@ -60,18 +60,14 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt)
qdisc = qdisc_create_dflt(dev_queue, default_qdisc_ops,
TC_H_MAKE(TC_H_MAJ(sch->handle),
TC_H_MIN(ntx + 1)));
- if (qdisc == NULL)
- goto err;
+ if (!qdisc)
+ return -ENOMEM;
priv->qdiscs[ntx] = qdisc;
qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
}
sch->flags |= TCQ_F_MQROOT;
return 0;
-
-err:
- mq_destroy(sch);
- return -ENOMEM;
}
static void mq_attach(struct Qdisc *sch)
diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c
index ad70ecf57ce7..66bccc5ff4ea 100644
--- a/net/sched/sch_mqprio.c
+++ b/net/sched/sch_mqprio.c
@@ -117,20 +117,17 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
/* pre-allocate qdisc, attachment can't fail */
priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]),
GFP_KERNEL);
- if (priv->qdiscs == NULL) {
- err = -ENOMEM;
- goto err;
- }
+ if (!priv->qdiscs)
+ return -ENOMEM;
for (i = 0; i < dev->num_tx_queues; i++) {
dev_queue = netdev_get_tx_queue(dev, i);
qdisc = qdisc_create_dflt(dev_queue, default_qdisc_ops,
TC_H_MAKE(TC_H_MAJ(sch->handle),
TC_H_MIN(i + 1)));
- if (qdisc == NULL) {
- err = -ENOMEM;
- goto err;
- }
+ if (!qdisc)
+ return -ENOMEM;
+
priv->qdiscs[i] = qdisc;
qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
}
@@ -143,7 +140,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
priv->hw_owned = 1;
err = dev->netdev_ops->ndo_setup_tc(dev, qopt->num_tc);
if (err)
- goto err;
+ return err;
} else {
netdev_set_num_tc(dev, qopt->num_tc);
for (i = 0; i < qopt->num_tc; i++)
@@ -157,10 +154,6 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
sch->flags |= TCQ_F_MQROOT;
return 0;
-
-err:
- mqprio_destroy(sch);
- return err;
}
static void mqprio_attach(struct Qdisc *sch)
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 498f0a2cb47f..3f2c3eed04da 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -434,6 +434,7 @@ congestion_drop:
qdisc_drop(head, sch);
slot_queue_add(slot, skb);
+ qdisc_tree_reduce_backlog(sch, 0, delta);
return NET_XMIT_CN;
}
@@ -465,8 +466,10 @@ enqueue:
/* Return Congestion Notification only if we dropped a packet
* from this flow.
*/
- if (qlen != slot->qlen)
+ if (qlen != slot->qlen) {
+ qdisc_tree_reduce_backlog(sch, 0, dropped - qdisc_pkt_len(skb));
return NET_XMIT_CN;
+ }
/* As we dropped a packet, better let upper stack know this */
qdisc_tree_reduce_backlog(sch, 1, dropped);
@@ -742,9 +745,10 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
q->ht = sfq_alloc(sizeof(q->ht[0]) * q->divisor);
q->slots = sfq_alloc(sizeof(q->slots[0]) * q->maxflows);
if (!q->ht || !q->slots) {
- sfq_destroy(sch);
+ /* Note: sfq_destroy() will be called by our caller */
return -ENOMEM;
}
+
for (i = 0; i < q->divisor; i++)
q->ht[i] = SFQ_EMPTY_SLOT;
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 7527c168e471..e33e9bd4ed5a 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -510,7 +510,9 @@ static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr,
{
addr->sa.sa_family = AF_INET6;
addr->v6.sin6_port = port;
+ addr->v6.sin6_flowinfo = 0;
addr->v6.sin6_addr = *saddr;
+ addr->v6.sin6_scope_id = 0;
}
/* Compare addresses exactly.
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
index a0c90572d0e5..f86c6555a539 100644
--- a/net/tipc/netlink_compat.c
+++ b/net/tipc/netlink_compat.c
@@ -258,13 +258,15 @@ static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
arg = nlmsg_new(0, GFP_KERNEL);
if (!arg) {
kfree_skb(msg->rep);
+ msg->rep = NULL;
return -ENOMEM;
}
err = __tipc_nl_compat_dumpit(cmd, msg, arg);
- if (err)
+ if (err) {
kfree_skb(msg->rep);
-
+ msg->rep = NULL;
+ }
kfree_skb(arg);
return err;
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index 9ff010cee67e..0727a6e9f780 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -539,6 +539,12 @@ country GH: DFS-FCC
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+country GI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
country GL: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
@@ -679,10 +685,6 @@ country IN:
(5170 - 5330 @ 160), (23)
(5735 - 5835 @ 80), (30)
-country IR:
- (2402 - 2482 @ 40), (20)
- (5735 - 5835 @ 80), (30)
-
country IS: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
@@ -731,7 +733,7 @@ country JO:
(5170 - 5250 @ 80), (23)
(5735 - 5835 @ 80), (23)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
- ((57000 - 66000 @ 2160), (40)
+ (57000 - 66000 @ 2160), (40)
country JP: DFS-JP
(2402 - 2482 @ 40), (20)
@@ -1417,7 +1419,7 @@ country UG: DFS-FCC
country US: DFS-FCC
(2402 - 2472 @ 40), (30)
- (5170 - 5250 @ 80), (24), AUTO-BW
+ (5170 - 5250 @ 80), (30), AUTO-BW
(5250 - 5330 @ 80), (24), DFS, AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d0d09c290ff8..2a9ec3e05c73 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -302,8 +302,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
[NL80211_ATTR_PID] = { .type = NLA_U32 },
[NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
- [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
- .len = WLAN_PMKID_LEN },
+ [NL80211_ATTR_PMKID] = { .len = WLAN_PMKID_LEN },
[NL80211_ATTR_DURATION] = { .type = NLA_U32 },
[NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
[NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
@@ -359,6 +358,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
[NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
+ [NL80211_ATTR_LOCAL_MESH_POWER_MODE] = {. type = NLA_U32 },
[NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
@@ -6124,6 +6124,10 @@ static int validate_scan_freqs(struct nlattr *freqs)
struct nlattr *attr1, *attr2;
int n_channels = 0, tmp1, tmp2;
+ nla_for_each_nested(attr1, freqs, tmp1)
+ if (nla_len(attr1) != sizeof(u32))
+ return 0;
+
nla_for_each_nested(attr1, freqs, tmp1) {
n_channels++;
/*
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 0f45e1a3a7d1..c8700399d7fd 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1214,7 +1214,7 @@ static inline int policy_to_flow_dir(int dir)
}
static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
- const struct flowi *fl)
+ const struct flowi *fl, u16 family)
{
struct xfrm_policy *pol;
struct net *net = sock_net(sk);
@@ -1223,8 +1223,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
read_lock_bh(&net->xfrm.xfrm_policy_lock);
pol = rcu_dereference(sk->sk_policy[dir]);
if (pol != NULL) {
- bool match = xfrm_selector_match(&pol->selector, fl,
- sk->sk_family);
+ bool match = xfrm_selector_match(&pol->selector, fl, family);
int err = 0;
if (match) {
@@ -2171,7 +2170,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
sk = sk_const_to_full_sk(sk);
if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
num_pols = 1;
- pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
+ pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family);
err = xfrm_expand_policies(fl, family, pols,
&num_pols, &num_xfrms);
if (err < 0)
@@ -2450,7 +2449,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
pol = NULL;
sk = sk_to_full_sk(sk);
if (sk && sk->sk_policy[dir]) {
- pol = xfrm_sk_policy_lookup(sk, dir, &fl);
+ pol = xfrm_sk_policy_lookup(sk, dir, &fl, family);
if (IS_ERR(pol)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
return 0;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 7a5a64e70b4d..4c696d4d5ce3 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1691,6 +1691,10 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
struct sk_buff *skb;
int err;
+ err = verify_policy_dir(dir);
+ if (err)
+ return ERR_PTR(err);
+
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
return ERR_PTR(-ENOMEM);
@@ -2216,6 +2220,10 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
int n = 0;
struct net *net = sock_net(skb->sk);
+ err = verify_policy_dir(pi->dir);
+ if (err)
+ return err;
+
if (attrs[XFRMA_MIGRATE] == NULL)
return -EINVAL;
@@ -2331,6 +2339,11 @@ static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
{
struct net *net = &init_net;
struct sk_buff *skb;
+ int err;
+
+ err = verify_policy_dir(dir);
+ if (err)
+ return err;
skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC);
if (skb == NULL)
@@ -2985,6 +2998,11 @@ out_free_skb:
static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
{
+ int err;
+
+ err = verify_policy_dir(dir);
+ if (err)
+ return err;
switch (c->event) {
case XFRM_MSG_NEWPOLICY:
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 46881f329561..17c55e512de6 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -125,7 +125,7 @@ endif
#
ifeq ($(CONFIG_KASAN),y)
_c_flags += $(if $(patsubst n%,, \
- $(KASAN_SANITIZE_$(basetarget).o)$(KASAN_SANITIZE)$(CONFIG_KASAN_SANITIZE_ALL)), \
+ $(KASAN_SANITIZE_$(basetarget).o)$(KASAN_SANITIZE)y), \
$(CFLAGS_KASAN))
endif
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index c72bcb33496a..02996be239bc 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -3410,7 +3410,7 @@ sub process {
$fixedline =~ s/\s*=\s*$/ = {/;
fix_insert_line($fixlinenr, $fixedline);
$fixedline = $line;
- $fixedline =~ s/^(.\s*){\s*/$1/;
+ $fixedline =~ s/^(.\s*)\{\s*/$1/;
fix_insert_line($fixlinenr, $fixedline);
}
}
@@ -3760,7 +3760,7 @@ sub process {
my $fixedline = rtrim($prevrawline) . " {";
fix_insert_line($fixlinenr, $fixedline);
$fixedline = $rawline;
- $fixedline =~ s/^(.\s*){\s*/$1\t/;
+ $fixedline =~ s/^(.\s*)\{\s*/$1\t/;
if ($fixedline !~ /^\+\s*$/) {
fix_insert_line($fixlinenr, $fixedline);
}
@@ -4249,7 +4249,7 @@ sub process {
if (ERROR("SPACING",
"space required before the open brace '{'\n" . $herecurr) &&
$fix) {
- $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\))){/$1 {/;
+ $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\)))\{/$1 {/;
}
}
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 696ccfa08d10..31898856682e 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -428,7 +428,7 @@ static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key,
static struct key *request_master_key(struct encrypted_key_payload *epayload,
const u8 **master_key, size_t *master_keylen)
{
- struct key *mkey = NULL;
+ struct key *mkey = ERR_PTR(-EINVAL);
if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX,
KEY_TRUSTED_PREFIX_LEN)) {
diff --git a/sound/core/control.c b/sound/core/control.c
index b4fe9b002512..bd01d492f46a 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1126,7 +1126,7 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
mutex_lock(&ue->card->user_ctl_lock);
change = ue->tlv_data_size != size;
if (!change)
- change = memcmp(ue->tlv_data, new_data, size);
+ change = memcmp(ue->tlv_data, new_data, size) != 0;
kfree(ue->tlv_data);
ue->tlv_data = new_data;
ue->tlv_data_size = size;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 1f062aaa5414..9af294c72a4d 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -654,7 +654,9 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
if (substream->ops->hw_free)
result = substream->ops->hw_free(substream);
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
- pm_qos_remove_request(&substream->latency_pm_qos_req);
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+
return result;
}
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index c67f9c212dd1..e326c1d80416 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1530,19 +1530,14 @@ static int snd_seq_ioctl_create_queue(struct snd_seq_client *client,
void __user *arg)
{
struct snd_seq_queue_info info;
- int result;
struct snd_seq_queue *q;
if (copy_from_user(&info, arg, sizeof(info)))
return -EFAULT;
- result = snd_seq_queue_alloc(client->number, info.locked, info.flags);
- if (result < 0)
- return result;
-
- q = queueptr(result);
- if (q == NULL)
- return -EINVAL;
+ q = snd_seq_queue_alloc(client->number, info.locked, info.flags);
+ if (IS_ERR(q))
+ return PTR_ERR(q);
info.queue = q->queue;
info.locked = q->locked;
@@ -1552,7 +1547,7 @@ static int snd_seq_ioctl_create_queue(struct snd_seq_client *client,
if (! info.name[0])
snprintf(info.name, sizeof(info.name), "Queue-%d", q->queue);
strlcpy(q->name, info.name, sizeof(q->name));
- queuefree(q);
+ snd_use_lock_free(&q->use_lock);
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 450c5187eecb..79e0c5604ef8 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -184,22 +184,26 @@ void __exit snd_seq_queues_delete(void)
static void queue_use(struct snd_seq_queue *queue, int client, int use);
/* allocate a new queue -
- * return queue index value or negative value for error
+ * return pointer to new queue or ERR_PTR(-errno) for error
+ * The new queue's use_lock is set to 1. It is the caller's responsibility to
+ * call snd_use_lock_free(&q->use_lock).
*/
-int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
+struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
{
struct snd_seq_queue *q;
q = queue_new(client, locked);
if (q == NULL)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
q->info_flags = info_flags;
queue_use(q, client, 1);
+ snd_use_lock_use(&q->use_lock);
if (queue_list_add(q) < 0) {
+ snd_use_lock_free(&q->use_lock);
queue_delete(q);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
- return q->queue;
+ return q;
}
/* delete a queue - queue must be owned by the client */
diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
index 30c8111477f6..719093489a2c 100644
--- a/sound/core/seq/seq_queue.h
+++ b/sound/core/seq/seq_queue.h
@@ -71,7 +71,7 @@ void snd_seq_queues_delete(void);
/* create new queue (constructor) */
-int snd_seq_queue_alloc(int client, int locked, unsigned int flags);
+struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int flags);
/* delete queue (destructor) */
int snd_seq_queue_delete(int client, int queueid);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 46f7b023f69c..ac5de4365e15 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -854,6 +854,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI),
SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 46a34039ecdc..5cab24f52825 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2233,6 +2233,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3),
SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9060, "Sony Vaio VPCL14M1R", ALC882_FIXUP_NO_PRIMARY_HP),
SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP),
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index c1b87c5800b1..b3fddba4c084 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -936,7 +936,8 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
/* FLL pre-scaler */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4,
- NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
+ NAU8825_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8825_FLL_REF_DIV_SFT);
/* select divided VCO input */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
NAU8825_FLL_FILTER_SW_MASK, 0x0000);
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index dff8edb83bfd..a0b220726a63 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -114,7 +114,8 @@
#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0)
/* FLL4 (0x07) */
-#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
+#define NAU8825_FLL_REF_DIV_SFT 10
+#define NAU8825_FLL_REF_DIV_MASK (0x3 << NAU8825_FLL_REF_DIV_SFT)
/* FLL5 (0x08) */
#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14)
diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
index 025a592b4015..a01c781acdf1 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
+++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
@@ -2619,6 +2619,17 @@ static int msm_anlg_cdc_codec_enable_micbias(struct snd_soc_dapm_widget *w,
return 0;
}
+static void set_compander_mode(void *handle, int val)
+{
+ struct sdm660_cdc_priv *handle_cdc = handle;
+ struct snd_soc_codec *codec = handle_cdc->codec;
+
+ if (get_codec_version(handle_cdc) >= DIANGU) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+ 0x08, val);
+ };
+}
static void update_clkdiv(void *handle, int val)
{
struct sdm660_cdc_priv *handle_cdc = handle;
@@ -3662,18 +3673,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 +3718,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 +4170,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);
@@ -4649,6 +4647,7 @@ static int msm_anlg_cdc_probe(struct platform_device *pdev)
BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier_mbhc);
sdm660_cdc->dig_plat_data.handle = (void *) sdm660_cdc;
+ sdm660_cdc->dig_plat_data.set_compander_mode = set_compander_mode;
sdm660_cdc->dig_plat_data.update_clkdiv = update_clkdiv;
sdm660_cdc->dig_plat_data.get_cdc_version = get_cdc_version;
sdm660_cdc->dig_plat_data.register_notifier =
diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
index 9563565f36d2..d07d1bee4d6b 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
+++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
@@ -168,6 +168,7 @@ struct msm_dig_ctrl_data {
struct msm_dig_ctrl_platform_data {
void *handle;
+ void (*set_compander_mode)(void *handle, int val);
void (*update_clkdiv)(void *handle, int val);
int (*get_cdc_version)(void *handle);
int (*register_notifier)(void *handle,
diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
index 4249ada17c87..25c318c6c4e1 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
+++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
@@ -214,60 +214,66 @@ static int msm_dig_cdc_codec_config_compander(struct snd_soc_codec *codec,
int interp_n, int event)
{
struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec);
+ int comp_ch_bits_set = 0x03;
dev_dbg(codec->dev, "%s: event %d shift %d, enabled %d\n",
__func__, event, interp_n,
dig_cdc->comp_enabled[interp_n]);
- /* compander is not enabled */
- if (!dig_cdc->comp_enabled[interp_n])
+ /* compander is invalid */
+ if (dig_cdc->comp_enabled[interp_n] != COMPANDER_1 &&
+ dig_cdc->comp_enabled[interp_n]) {
+ dev_dbg(codec->dev, "%s: Invalid compander %d\n", __func__,
+ dig_cdc->comp_enabled[interp_n]);
return 0;
+ }
- switch (dig_cdc->comp_enabled[interp_n]) {
- case COMPANDER_1:
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- /* Enable Compander Clock */
- snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09);
- snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01);
- snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_COMP0_B1_CTL,
- 1 << interp_n, 1 << interp_n);
- snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01);
- snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0x50);
- /* add sleep for compander to settle */
- usleep_range(1000, 1100);
- snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x28);
- snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0xB0);
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* compander is not enabled */
+ if (!dig_cdc->comp_enabled[interp_n]) {
+ dig_cdc->set_compander_mode(dig_cdc->handle, 0x00);
+ return 0;
+ };
+ dig_cdc->set_compander_mode(dig_cdc->handle, 0x08);
+ /* Enable Compander Clock */
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B1_CTL,
+ 1 << interp_n, 1 << interp_n);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0x50);
+ /* add sleep for compander to settle */
+ usleep_range(1000, 1100);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x28);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0xB0);
- /* Enable Compander GPIO */
- if (dig_cdc->codec_hph_comp_gpio)
- dig_cdc->codec_hph_comp_gpio(1, codec);
- } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
- /* Disable Compander GPIO */
- if (dig_cdc->codec_hph_comp_gpio)
- dig_cdc->codec_hph_comp_gpio(0, codec);
+ /* Enable Compander GPIO */
+ if (dig_cdc->codec_hph_comp_gpio)
+ dig_cdc->codec_hph_comp_gpio(1, codec);
+ } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ /* Disable Compander GPIO */
+ if (dig_cdc->codec_hph_comp_gpio)
+ dig_cdc->codec_hph_comp_gpio(0, codec);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B1_CTL,
+ 1 << interp_n, 0);
+ comp_ch_bits_set = snd_soc_read(codec,
+ MSM89XX_CDC_CORE_COMP0_B1_CTL);
+ if ((comp_ch_bits_set & 0x03) == 0x00) {
snd_soc_update_bits(codec,
MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x05);
- snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_COMP0_B1_CTL,
- 1 << interp_n, 0);
- snd_soc_update_bits(codec,
+ snd_soc_update_bits(codec,
MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x00);
}
- break;
- default:
- dev_dbg(codec->dev, "%s: Invalid compander %d\n", __func__,
- dig_cdc->comp_enabled[interp_n]);
- break;
- };
-
+ }
return 0;
}
@@ -1327,6 +1333,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 +1347,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"},
@@ -2101,6 +2113,7 @@ static int msm_dig_cdc_probe(struct platform_device *pdev)
msm_dig_cdc->dig_base, &msm_digital_regmap_config);
msm_dig_cdc->update_clkdiv = pdata->update_clkdiv;
+ msm_dig_cdc->set_compander_mode = pdata->set_compander_mode;
msm_dig_cdc->get_cdc_version = pdata->get_cdc_version;
msm_dig_cdc->handle = pdata->handle;
msm_dig_cdc->register_notifier = pdata->register_notifier;
diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h
index f0e7a9cf9228..11f36f99f1bd 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h
+++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h
@@ -49,6 +49,7 @@ struct msm_dig_priv {
u32 mute_mask;
int dapm_bias_off;
void *handle;
+ void (*set_compander_mode)(void *handle, int val);
void (*update_clkdiv)(void *handle, int val);
int (*get_cdc_version)(void *handle);
int (*register_notifier)(void *handle,
@@ -58,6 +59,7 @@ struct msm_dig_priv {
struct dig_ctrl_platform_data {
void *handle;
+ void (*set_compander_mode)(void *handle, int val);
void (*update_clkdiv)(void *handle, int val);
int (*get_cdc_version)(void *handle);
int (*register_notifier)(void *handle,
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index a564759845f9..5a3f544bb3a8 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -126,6 +126,16 @@ static const struct reg_default aic3x_reg[] = {
{ 108, 0x00 }, { 109, 0x00 },
};
+static bool aic3x_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AIC3X_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
static const struct regmap_config aic3x_regmap = {
.reg_bits = 8,
.val_bits = 8,
@@ -133,6 +143,9 @@ static const struct regmap_config aic3x_regmap = {
.max_register = DAC_ICC_ADJ,
.reg_defaults = aic3x_reg,
.num_reg_defaults = ARRAY_SIZE(aic3x_reg),
+
+ .volatile_reg = aic3x_volatile_reg,
+
.cache_type = REGCACHE_RBTREE,
};
diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c
index 9b1c8c98946c..1613c5baa9c7 100644
--- a/sound/soc/codecs/wcd-dsp-mgr.c
+++ b/sound/soc/codecs/wcd-dsp-mgr.c
@@ -415,22 +415,24 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
/* Go through the list of segments and download one by one */
list_for_each_entry(seg, wdsp->seg_list, list) {
ret = wdsp_load_each_segment(wdsp, seg);
- if (IS_ERR_VALUE(ret)) {
- wdsp_broadcast_event_downseq(wdsp,
- WDSP_EVENT_DLOAD_FAILED,
- NULL);
+ if (ret)
goto dload_error;
- }
}
+ /* Flush the list before setting status and notifying components */
+ wdsp_flush_segment_list(wdsp->seg_list);
+
WDSP_SET_STATUS(wdsp, status);
/* Notify all components that image is downloaded */
wdsp_broadcast_event_downseq(wdsp, post, NULL);
+done:
+ return ret;
dload_error:
wdsp_flush_segment_list(wdsp->seg_list);
-done:
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, NULL);
+
return ret;
}
@@ -484,10 +486,14 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
/* Make sure wdsp is in good state */
if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) {
WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status);
- ret = -EINVAL;
- goto done;
+ return -EINVAL;
}
+ /*
+ * Acquire SSR mutex lock to make sure enablement of DSP
+ * does not race with SSR handling.
+ */
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
/* Download the read-write sections of image */
ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE);
if (IS_ERR_VALUE(ret)) {
@@ -508,6 +514,7 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL);
WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED);
done:
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
return ret;
}
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
index e791bf07ec67..29c218013a07 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
@@ -763,10 +763,6 @@ static int wcd_control_handler(struct device *dev, void *priv_data,
case WDSP_EVENT_DLOAD_FAILED:
case WDSP_EVENT_POST_SHUTDOWN:
- if (event == WDSP_EVENT_POST_DLOAD_CODE)
- /* Mark DSP online since code download is complete */
- wcd_cntl_change_online_state(cntl, 1);
-
/* Disable CPAR */
wcd_cntl_cpar_ctrl(cntl, false);
/* Disable all the clocks */
@@ -775,6 +771,11 @@ static int wcd_control_handler(struct device *dev, void *priv_data,
dev_err(codec->dev,
"%s: Failed to disable clocks, err = %d\n",
__func__, ret);
+
+ if (event == WDSP_EVENT_POST_DLOAD_CODE)
+ /* Mark DSP online since code download is complete */
+ wcd_cntl_change_online_state(cntl, 1);
+
break;
case WDSP_EVENT_PRE_DLOAD_DATA:
diff --git a/sound/soc/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/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 54c33204541f..ff6fcd9f92f7 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -100,7 +100,7 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
if (ret && ret != -ENOTSUPP)
goto err;
}
-
+ return 0;
err:
return ret;
}
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/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c
index 2ae78f75c340..b1dff8764618 100644
--- a/sound/soc/msm/apq8096-auto.c
+++ b/sound/soc/msm/apq8096-auto.c
@@ -3481,12 +3481,13 @@ static struct snd_soc_dai_link apq8096_common_dai_links[] = {
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
},
{
- .name = "MSM8996 Compress9",
- .stream_name = "Compress9",
+ .name = "MSM8996 ULL NOIRQ 2",
+ .stream_name = "MM_NOIRQ_2",
.cpu_dai_name = "MultiMedia16",
- .platform_name = "msm-compress-dsp",
+ .platform_name = "msm-pcm-dsp-noirq",
.dynamic = 1,
.dpcm_playback = 1,
+ .dpcm_capture = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.codec_dai_name = "snd-soc-dummy-dai",
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 468afdc81424..5facafdc7729 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -2496,8 +2496,21 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.rate_min = 8000,
.rate_max = 384000,
},
+ .capture = {
+ .stream_name = "MultiMedia16 Capture",
+ .aif_name = "MM_UL16",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
.ops = &msm_fe_Multimedia_dai_ops,
- .compress_new = snd_soc_new_compress,
.name = "MultiMedia16",
.probe = fe_dai_probe,
},
@@ -2750,6 +2763,45 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.name = "MultiMedia25",
.probe = fe_dai_probe,
},
+ {
+ .playback = {
+ .stream_name = "MultiMedia26 Playback",
+ .aif_name = "MM_DL26",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia26",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "MultiMedia27 Capture",
+ .aif_name = "MM_UL27",
+ .rates = (SNDRV_PCM_RATE_8000_192000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia27",
+ .probe = fe_dai_probe,
+ },
};
static int msm_fe_dai_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/msm/msm-pcm-hostless.c b/sound/soc/msm/msm-pcm-hostless.c
index 57932433afe9..51b0a7208462 100644
--- a/sound/soc/msm/msm-pcm-hostless.c
+++ b/sound/soc/msm/msm-pcm-hostless.c
@@ -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
@@ -25,7 +25,9 @@ static int msm_pcm_hostless_prepare(struct snd_pcm_substream *substream)
pr_err("%s: invalid params\n", __func__);
return -EINVAL;
}
- pm_qos_remove_request(&substream->latency_pm_qos_req);
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+
return 0;
}
diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c
index 4eafd322d50d..010dfa3322a0 100644
--- a/sound/soc/msm/msm8996.c
+++ b/sound/soc/msm/msm8996.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2910,12 +2910,13 @@ static struct snd_soc_dai_link msm8996_common_dai_links[] = {
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
},
{
- .name = "MSM8996 Compress9",
- .stream_name = "Compress9",
+ .name = "MSM8996 ULL NOIRQ_2",
+ .stream_name = "MM_NOIRQ_2",
.cpu_dai_name = "MultiMedia16",
- .platform_name = "msm-compress-dsp",
+ .platform_name = "msm-pcm-dsp-noirq",
.dynamic = 1,
.dpcm_playback = 1,
+ .dpcm_capture = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.codec_dai_name = "snd-soc-dummy-dai",
diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c
index 8d737c3d3351..9159ea642816 100644
--- a/sound/soc/msm/msm8998.c
+++ b/sound/soc/msm/msm8998.c
@@ -434,7 +434,8 @@ static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32",
static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"};
static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16",
"KHZ_32", "KHZ_44P1", "KHZ_48",
- "KHZ_96", "KHZ_192"};
+ "KHZ_88P2", "KHZ_96", "KHZ_176P4",
+ "KHZ_192"};
static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four",
"Five", "Six", "Seven",
"Eight"};
@@ -2228,12 +2229,18 @@ static int mi2s_get_sample_rate_val(int sample_rate)
case SAMPLING_RATE_48KHZ:
sample_rate_val = 4;
break;
- case SAMPLING_RATE_96KHZ:
+ case SAMPLING_RATE_88P2KHZ:
sample_rate_val = 5;
break;
- case SAMPLING_RATE_192KHZ:
+ case SAMPLING_RATE_96KHZ:
sample_rate_val = 6;
break;
+ case SAMPLING_RATE_176P4KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 8;
+ break;
default:
sample_rate_val = 4;
break;
@@ -2262,9 +2269,15 @@ static int mi2s_get_sample_rate(int value)
sample_rate = SAMPLING_RATE_48KHZ;
break;
case 5:
- sample_rate = SAMPLING_RATE_96KHZ;
+ sample_rate = SAMPLING_RATE_88P2KHZ;
break;
case 6:
+ sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ sample_rate = SAMPLING_RATE_176P4KHZ;
+ break;
+ case 8:
sample_rate = SAMPLING_RATE_192KHZ;
break;
default:
@@ -5315,12 +5328,13 @@ static struct snd_soc_dai_link msm_common_dai_links[] = {
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
},
{
- .name = MSM_DAILINK_NAME(Compress9),
- .stream_name = "Compress9",
+ .name = MSM_DAILINK_NAME(ULL_NOIRQ_2),
+ .stream_name = "MM_NOIRQ_2",
.cpu_dai_name = "MultiMedia16",
- .platform_name = "msm-compress-dsp",
+ .platform_name = "msm-pcm-dsp-noirq",
.dynamic = 1,
.dpcm_playback = 1,
+ .dpcm_capture = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.codec_dai_name = "snd-soc-dummy-dai",
@@ -5509,7 +5523,7 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = {
{
.name = MSM_DAILINK_NAME(Transcode Loopback Playback),
.stream_name = "Transcode Loopback Playback",
- .cpu_dai_name = "MultiMedia14",
+ .cpu_dai_name = "MultiMedia26",
.platform_name = "msm-transcode-loopback",
.dynamic = 1,
.dpcm_playback = 1,
@@ -5520,12 +5534,12 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = {
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
/* this dainlink has playback support */
- .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA26,
},
{
.name = MSM_DAILINK_NAME(Transcode Loopback Capture),
.stream_name = "Transcode Loopback Capture",
- .cpu_dai_name = "MultiMedia18",
+ .cpu_dai_name = "MultiMedia27",
.platform_name = "msm-transcode-loopback",
.dynamic = 1,
.dpcm_capture = 1,
@@ -5535,7 +5549,7 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = {
.codec_name = "snd-soc-dummy",
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
- .be_id = MSM_FRONTEND_DAI_MULTIMEDIA18,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA27,
},
{
.name = "MultiMedia21",
diff --git a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c
index e312a879b86a..1286d3185780 100644
--- a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c
@@ -155,7 +155,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"VIRT ENABLE", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_VIRTUALIZER;
*updt_params++ =
@@ -183,7 +183,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"VIRT STRENGTH", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_VIRTUALIZER;
*updt_params++ =
@@ -211,7 +211,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"VIRT OUT_TYPE", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_VIRTUALIZER;
*updt_params++ =
@@ -239,7 +239,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"VIRT GAIN_ADJUST", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_VIRTUALIZER;
*updt_params++ =
@@ -318,7 +318,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_ENABLE", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -346,7 +346,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_MODE", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -374,7 +374,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_PRESET", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -402,7 +402,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_WET_MIX", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -430,7 +430,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_GAIN_ADJUST", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -458,7 +458,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_ROOM_LEVEL", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -486,7 +486,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_ROOM_HF_LEVEL", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -514,7 +514,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_DECAY_TIME", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -542,7 +542,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_DECAY_HF_RATIO", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -570,7 +570,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_REFLECTIONS_LEVEL", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -598,7 +598,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_REFLECTIONS_DELAY", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -626,7 +626,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_LEVEL", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -654,7 +654,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_DELAY", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -682,7 +682,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_DIFFUSION", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -710,7 +710,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"REVERB_DENSITY", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_REVERB;
*updt_params++ =
@@ -790,7 +790,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"BASS_BOOST_ENABLE", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_BASS_BOOST;
*updt_params++ =
@@ -818,7 +818,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"BASS_BOOST_MODE", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_BASS_BOOST;
*updt_params++ =
@@ -846,7 +846,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"BASS_BOOST_STRENGTH", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_BASS_BOOST;
*updt_params++ =
@@ -924,7 +924,7 @@ int msm_audio_effects_pbe_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"PBE_ENABLE", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_PBE;
*updt_params++ =
@@ -950,7 +950,7 @@ int msm_audio_effects_pbe_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"PBE_PARAM", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_PBE;
*updt_params++ =
@@ -1035,7 +1035,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"EQ_ENABLE", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
*updt_params++ =
@@ -1103,7 +1103,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"EQ_CONFIG", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
*updt_params++ =
@@ -1154,7 +1154,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"EQ_BAND_INDEX", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
*updt_params++ =
@@ -1186,7 +1186,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac,
MAX_INBAND_PARAM_SZ,
"EQ_SINGLE_BAND_FREQ", rc);
if (rc != 0)
- break;
+ goto invalid_config;
*updt_params++ =
AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
*updt_params++ =
@@ -1276,7 +1276,7 @@ static int __msm_audio_effects_volume_handler(struct audio_client *ac,
"VOLUME/VOLUME2_GAIN_2CH",
rc);
if (rc != 0)
- break;
+ goto invalid_config;
if (instance == SOFT_VOLUME_INSTANCE_2)
*updt_params++ =
ASM_MODULE_ID_VOL_CTRL2;
@@ -1325,7 +1325,7 @@ static int __msm_audio_effects_volume_handler(struct audio_client *ac,
"VOLUME/VOLUME2_GAIN_MASTER",
rc);
if (rc != 0)
- break;
+ goto invalid_config;
if (instance == SOFT_VOLUME_INSTANCE_2)
*updt_params++ =
ASM_MODULE_ID_VOL_CTRL2;
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 0acf6e8ffe49..c462f682e160 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -39,6 +39,7 @@
#include <sound/apr_audio-v2.h>
#include <sound/q6asm-v2.h>
+#include <sound/q6core.h>
#include <sound/compress_params.h>
#include <sound/compress_offload.h>
#include <sound/compress_driver.h>
@@ -103,6 +104,7 @@ struct msm_compr_pdata {
bool use_legacy_api; /* indicates use older asm apis*/
struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX];
struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX];
+ int32_t ion_fd[MSM_FRONTEND_DAI_MAX];
};
struct msm_compr_audio {
@@ -156,6 +158,12 @@ struct msm_compr_audio {
uint32_t start_delay_lsw;
uint32_t start_delay_msw;
+ int32_t shm_ion_fd;
+ struct ion_client *lib_ion_client;
+ struct ion_client *shm_ion_client;
+ struct ion_handle *lib_ion_handle;
+ struct ion_handle *shm_ion_handle;
+
uint64_t marker_timestamp;
struct msm_compr_gapless_state gapless_state;
@@ -1506,6 +1514,65 @@ static int msm_compr_configure_dsp_for_capture(struct snd_compr_stream *cstream)
return ret;
}
+static int msm_compr_map_ion_fd(struct msm_compr_audio *prtd, int fd)
+{
+ ion_phys_addr_t paddr;
+ size_t pa_len = 0;
+ int ret = 0;
+
+ ret = msm_audio_ion_phys_assign("audio_lib_mem_client",
+ &prtd->lib_ion_client,
+ &prtd->lib_ion_handle,
+ fd, &paddr, &pa_len, HLOS_TO_ADSP);
+ if (ret) {
+ pr_err("%s: audio lib ION phys failed, rc = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = q6core_add_remove_pool_pages(paddr, pa_len,
+ ADSP_MEMORY_MAP_HLOS_PHYSPOOL, true);
+ if (ret) {
+ pr_err("%s: add remove pages failed, rc = %d\n", __func__, ret);
+ /* Assign back to HLOS if add pages cmd failed */
+ msm_audio_ion_phys_free(prtd->lib_ion_client,
+ prtd->lib_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ }
+
+done:
+ return ret;
+}
+
+static int msm_compr_unmap_ion_fd(struct msm_compr_audio *prtd)
+{
+ ion_phys_addr_t paddr;
+ size_t pa_len = 0;
+ int ret = 0;
+
+ if (!prtd->lib_ion_client || !prtd->lib_ion_handle) {
+ pr_err("%s: ion_client or ion_handle is NULL", __func__);
+ return -EINVAL;
+ }
+
+ ret = msm_audio_ion_phys_free(prtd->lib_ion_client,
+ prtd->lib_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ if (ret) {
+ pr_err("%s: audio lib ION phys failed, rc = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = q6core_add_remove_pool_pages(paddr, pa_len,
+ ADSP_MEMORY_MAP_HLOS_PHYSPOOL, false);
+ if (ret)
+ pr_err("%s: add remove pages failed, rc = %d\n", __func__, ret);
+
+done:
+ return ret;
+}
+
static int msm_compr_playback_open(struct snd_compr_stream *cstream)
{
struct snd_compr_runtime *runtime = cstream->runtime;
@@ -1513,6 +1580,7 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream)
struct msm_compr_audio *prtd;
struct msm_compr_pdata *pdata =
snd_soc_platform_get_drvdata(rtd->platform);
+ int ret = 0;
pr_debug("%s\n", __func__);
prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL);
@@ -1528,19 +1596,16 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream)
kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL);
if (!pdata->audio_effects[rtd->dai_link->be_id]) {
pr_err("%s: Could not allocate memory for effects\n", __func__);
- pdata->cstream[rtd->dai_link->be_id] = NULL;
- kfree(prtd);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto effect_err;
}
pdata->dec_params[rtd->dai_link->be_id] =
kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL);
if (!pdata->dec_params[rtd->dai_link->be_id]) {
pr_err("%s: Could not allocate memory for dec params\n",
__func__);
- kfree(pdata->audio_effects[rtd->dai_link->be_id]);
- pdata->cstream[rtd->dai_link->be_id] = NULL;
- kfree(prtd);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto param_err;
}
prtd->codec = FORMAT_MP3;
prtd->bytes_received = 0;
@@ -1584,19 +1649,32 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream)
(app_cb)compr_event_handler, prtd);
if (!prtd->audio_client) {
pr_err("%s: Could not allocate memory for client\n", __func__);
- kfree(pdata->audio_effects[rtd->dai_link->be_id]);
- kfree(pdata->dec_params[rtd->dai_link->be_id]);
- pdata->cstream[rtd->dai_link->be_id] = NULL;
- runtime->private_data = NULL;
- kfree(prtd);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto ac_err;
}
pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
prtd->audio_client->perf_mode = false;
prtd->session_id = prtd->audio_client->session;
msm_adsp_init_mixer_ctl_pp_event_queue(rtd);
-
+ if (pdata->ion_fd[rtd->dai_link->be_id] > 0) {
+ ret = msm_compr_map_ion_fd(prtd,
+ pdata->ion_fd[rtd->dai_link->be_id]);
+ if (ret < 0)
+ goto map_err;
+ }
return 0;
+
+map_err:
+ q6asm_audio_client_free(prtd->audio_client);
+ac_err:
+ kfree(pdata->dec_params[rtd->dai_link->be_id]);
+param_err:
+ kfree(pdata->audio_effects[rtd->dai_link->be_id]);
+effect_err:
+ pdata->cstream[rtd->dai_link->be_id] = NULL;
+ runtime->private_data = NULL;
+ kfree(prtd);
+ return ret;
}
static int msm_compr_capture_open(struct snd_compr_stream *cstream)
@@ -1675,6 +1753,8 @@ static int msm_compr_playback_free(struct snd_compr_stream *cstream)
int dir = IN, ret = 0, stream_id;
unsigned long flags;
uint32_t stream_index;
+ ion_phys_addr_t paddr;
+ size_t pa_len = 0;
pr_debug("%s\n", __func__);
@@ -1748,6 +1828,14 @@ static int msm_compr_playback_free(struct snd_compr_stream *cstream)
}
q6asm_audio_client_buf_free_contiguous(dir, ac);
+ if (prtd->shm_ion_fd > 0)
+ msm_audio_ion_phys_free(prtd->shm_ion_client,
+ prtd->shm_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ if (pdata->ion_fd[soc_prtd->dai_link->be_id] > 0) {
+ msm_compr_unmap_ion_fd(prtd);
+ pdata->ion_fd[soc_prtd->dai_link->be_id] = 0;
+ }
q6asm_audio_client_free(ac);
msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd);
@@ -3655,7 +3743,120 @@ done:
return ret;
}
-static int msm_compr_ion_fd_map_put(struct snd_kcontrol *kcontrol,
+static int msm_compr_playback_dnmix_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int len = 0;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd;
+ struct asm_stream_pan_ctrl_params dnmix_param;
+ int be_id = ucontrol->value.integer.value[len++];
+ int stream_id = 0;
+ /*
+ * Max index for this mixer control includes below
+ * be_id (1)
+ * num_output_channels (1)
+ * num_input_channels (1)
+ * output ch map (max) (8)
+ * input ch map (max) (8)
+ * mix matrix coefficients (max)(64)
+ */
+ int max_index = 0;
+ int max_mixer_ctrl_value_size = 128;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ if (cstream == NULL) {
+ pr_err("%s cstream is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: prtd is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client == NULL) {
+ pr_err("%s: audio_client is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ stream_id = prtd->audio_client->session;
+ if (len >= max_mixer_ctrl_value_size) {
+ ret = -EINVAL;
+ goto done;
+ }
+ dnmix_param.num_output_channels =
+ ucontrol->value.integer.value[len++];
+ if (dnmix_param.num_output_channels >
+ PCM_FORMAT_MAX_NUM_CHANNEL) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (len >= max_mixer_ctrl_value_size) {
+ ret = -EINVAL;
+ goto done;
+ }
+ dnmix_param.num_input_channels =
+ ucontrol->value.integer.value[len++];
+ if (dnmix_param.num_input_channels >
+ PCM_FORMAT_MAX_NUM_CHANNEL) {
+ ret = -EINVAL;
+ goto done;
+ }
+ max_index = len + dnmix_param.num_output_channels +
+ dnmix_param.num_input_channels +
+ dnmix_param.num_output_channels *
+ dnmix_param.num_input_channels;
+ if (max_index >= max_mixer_ctrl_value_size) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (ucontrol->value.integer.value[len++]) {
+ for (i = 0; i < dnmix_param.num_output_channels; i++) {
+ dnmix_param.output_channel_map[i] =
+ ucontrol->value.integer.value[len++];
+ }
+ }
+ if (ucontrol->value.integer.value[len++]) {
+ for (i = 0; i < dnmix_param.num_input_channels; i++) {
+ dnmix_param.input_channel_map[i] =
+ ucontrol->value.integer.value[len++];
+ }
+ }
+ if (ucontrol->value.integer.value[len++]) {
+ for (i = 0; i < dnmix_param.num_output_channels *
+ dnmix_param.num_input_channels; i++) {
+ dnmix_param.gain[i] =
+ ucontrol->value.integer.value[len++];
+ }
+ }
+ msm_routing_set_downmix_control_data(be_id,
+ stream_id,
+ &dnmix_param);
+
+done:
+ return ret;
+}
+
+static int msm_compr_shm_ion_fd_map_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
@@ -3664,7 +3865,6 @@ static int msm_compr_ion_fd_map_put(struct snd_kcontrol *kcontrol,
snd_soc_component_get_drvdata(comp);
struct snd_compr_stream *cstream = NULL;
struct msm_compr_audio *prtd;
- int fd;
int ret = 0;
if (fe_id >= MSM_FRONTEND_DAI_MAX) {
@@ -3694,10 +3894,36 @@ static int msm_compr_ion_fd_map_put(struct snd_kcontrol *kcontrol,
goto done;
}
- memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd));
- ret = q6asm_send_ion_fd(prtd->audio_client, fd);
+ memcpy(&prtd->shm_ion_fd, ucontrol->value.bytes.data,
+ sizeof(prtd->shm_ion_fd));
+ ret = q6asm_audio_map_shm_fd(prtd->audio_client,
+ &prtd->shm_ion_client,
+ &prtd->shm_ion_handle, prtd->shm_ion_fd);
if (ret < 0)
- pr_err("%s: failed to register ion fd\n", __func__);
+ pr_err("%s: failed to map shm mem\n", __func__);
+done:
+ return ret;
+}
+
+static int msm_compr_lib_ion_fd_map_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ int ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&pdata->ion_fd[fe_id], ucontrol->value.bytes.data,
+ sizeof(pdata->ion_fd[fe_id]));
+
done:
return ret;
}
@@ -3901,6 +4127,16 @@ static int msm_compr_channel_map_info(struct snd_kcontrol *kcontrol,
return 0;
}
+static int msm_compr_device_downmix_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 128;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd)
{
const char *mixer_ctl_name = "Compress Playback";
@@ -4199,6 +4435,53 @@ static int msm_compr_add_dec_runtime_params_control(
return 0;
}
+static int msm_compr_add_device_down_mix_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *playback_mixer_ctl_name = "Audio Device";
+ const char *deviceNo = "NN";
+ const char *suffix = "Downmix Control";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new device_downmix_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_device_downmix_info,
+ .put = msm_compr_playback_dnmix_ctl_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ device_downmix_control[0].name = mixer_str;
+ device_downmix_control[0].private_value = rtd->dai_link->be_id;
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ device_downmix_control,
+ ARRAY_SIZE(device_downmix_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd)
{
const char *playback_mixer_ctl_name = "Audio Stream";
@@ -4329,7 +4612,7 @@ static int msm_compr_add_channel_map_control(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static int msm_compr_add_io_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
+static int msm_compr_add_shm_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
{
const char *mixer_ctl_name = "Playback ION FD";
const char *deviceNo = "NN";
@@ -4341,7 +4624,52 @@ static int msm_compr_add_io_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
.name = "?",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = msm_adsp_stream_cmd_info,
- .put = msm_compr_ion_fd_map_put,
+ .put = msm_compr_shm_ion_fd_map_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_ion_fd_config_control[0].name = mixer_str;
+ fe_ion_fd_config_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_ion_fd_config_control,
+ ARRAY_SIZE(fe_ion_fd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_compr_add_lib_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback ION LIB FD";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_ion_fd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_compr_lib_ion_fd_map_put,
.private_value = 0,
}
};
@@ -4442,16 +4770,26 @@ static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
pr_err("%s: Could not add Compr ADSP Stream Callback Control\n",
__func__);
- rc = msm_compr_add_io_fd_cmd_control(rtd);
+ rc = msm_compr_add_shm_ion_fd_cmd_control(rtd);
if (rc)
pr_err("%s: Could not add Compr ion fd Control\n",
__func__);
+ rc = msm_compr_add_lib_ion_fd_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr ion lib fd Control\n",
+ __func__);
+
rc = msm_compr_add_event_ack_cmd_control(rtd);
if (rc)
pr_err("%s: Could not add Compr event ack Control\n",
__func__);
+ rc = msm_compr_add_device_down_mix_controls(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr downmix Control\n",
+ __func__);
+
rc = msm_compr_add_query_audio_effect_control(rtd);
if (rc)
pr_err("%s: Could not add Compr Query Audio Effect Control\n",
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 3610901addea..292b3d04f7d5 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -4152,7 +4152,8 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 8000,
diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
index 3e72aa130c18..35270e3340ec 100644
--- a/sound/soc/msm/qdsp6v2/msm-lsm-client.c
+++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
@@ -1683,7 +1683,7 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
dev_err(rtd->dev,
"%s REG_SND_MODEL failed err %d\n",
__func__, err);
- return err;
+ goto done;
}
break;
case SNDRV_LSM_SET_PARAMS: {
@@ -1855,13 +1855,15 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
dev_err(rtd->dev,
"%s: Invalid params event_status_v3\n",
__func__);
- return -EINVAL;
+ err = -EINVAL;
+ goto done;
}
if (copy_from_user(&userarg, arg, sizeof(userarg))) {
dev_err(rtd->dev,
"%s: err copyuser event_status_v3\n",
__func__);
- return -EFAULT;
+ err = -EFAULT;
+ goto done;
}
if (userarg.payload_size >
@@ -1869,7 +1871,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
__func__, userarg.payload_size,
LISTEN_MAX_STATUS_PAYLOAD_SIZE);
- return -EINVAL;
+ err = -EINVAL;
+ goto done;
}
size = sizeof(struct snd_lsm_event_status_v3) +
@@ -1879,7 +1882,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
dev_err(rtd->dev,
"%s: Allocation failed event status size %d\n",
__func__, size);
- return -EFAULT;
+ err = -EFAULT;
+ goto done;
}
user->payload_size = userarg.payload_size;
err = msm_lsm_ioctl_shared(substream, cmd, user);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index de769e8b806c..b94eb6fbfeea 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -1668,7 +1668,7 @@ static int msm_pcm_playback_pan_scale_ctl_put(struct snd_kcontrol *kcontrol,
for (i = 0; i < pan_param.num_output_channels *
pan_param.num_input_channels; i++) {
pan_param.gain[i] =
- !(ucontrol->value.integer.value[len++] > 0) ?
+ !(ucontrol->value.integer.value[len++] > 0) ?
0 : 2 << 13;
}
}
@@ -1679,8 +1679,12 @@ static int msm_pcm_playback_pan_scale_ctl_put(struct snd_kcontrol *kcontrol,
pan_param.num_input_channels;
for (i = 0; i < pan_param.num_output_channels *
pan_param.num_input_channels; i++) {
+ /*
+ * The data userspace passes is already in Q14 format.
+ * For volume gain is in Q28.
+ */
pan_param.gain[i] =
- ucontrol->value.integer.value[len++];
+ ucontrol->value.integer.value[len++] << 14;
}
ret = q6asm_set_vol_ctrl_gain_pair(prtd->audio_client,
&pan_param);
@@ -1753,7 +1757,7 @@ static int msm_pcm_playback_dnmix_ctl_put(struct snd_kcontrol *kcontrol,
struct msm_audio *prtd;
struct asm_stream_pan_ctrl_params dnmix_param;
- int be_id = ucontrol->value.integer.value[len];
+ int be_id = ucontrol->value.integer.value[len++];
int stream_id = 0;
if (!usr_info) {
@@ -1809,7 +1813,7 @@ static int msm_pcm_playback_dnmix_ctl_put(struct snd_kcontrol *kcontrol,
for (i = 0; i < dnmix_param.num_output_channels *
dnmix_param.num_input_channels; i++) {
dnmix_param.gain[i] =
- ucontrol->value.integer.value[len++];
+ ucontrol->value.integer.value[len++];
}
}
msm_routing_set_downmix_control_data(be_id,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index b99549d62e8a..2114adae72d7 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -620,6 +620,12 @@ static struct msm_pcm_routing_fdai_data
/* MULTIMEDIA25 */
{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA26 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA27 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
/* CS_VOICE */
{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
@@ -3669,6 +3675,11 @@ static const struct snd_kcontrol_new ext_ec_ref_mux_ul9 =
msm_route_ec_ref_rx_enum[0],
msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul16 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL16 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
static const struct snd_kcontrol_new ext_ec_ref_mux_ul17 =
SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL17 MUX Mux",
msm_route_ec_ref_rx_enum[0],
@@ -3827,6 +3838,9 @@ static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = {
@@ -3887,6 +3901,9 @@ static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SEC_I2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = {
@@ -3947,7 +3964,9 @@ static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SPDIF_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
-
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = {
@@ -3999,6 +4018,9 @@ static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_2_RX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = {
@@ -4059,6 +4081,9 @@ static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SLIMBUS_5_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
@@ -4134,6 +4159,9 @@ static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia25", MSM_BACKEND_DAI_SLIMBUS_0_RX,
MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = {
@@ -4194,6 +4222,9 @@ static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = {
@@ -4254,6 +4285,9 @@ static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = {
@@ -4314,6 +4348,9 @@ static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = {
@@ -4368,6 +4405,9 @@ static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new secondary_mi2s_rx2_mixer_controls[] = {
@@ -4434,6 +4474,9 @@ static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = {
@@ -4494,6 +4537,9 @@ static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int0_mi2s_rx_mixer_controls[] = {
@@ -4545,6 +4591,9 @@ static const struct snd_kcontrol_new int0_mi2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT0_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int4_mi2s_rx_mixer_controls[] = {
@@ -4596,6 +4645,9 @@ static const struct snd_kcontrol_new int4_mi2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT4_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
@@ -4671,6 +4723,9 @@ static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia25", MSM_BACKEND_DAI_HDMI_RX,
MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new display_port_mixer_controls[] = {
@@ -4722,6 +4777,9 @@ static const struct snd_kcontrol_new display_port_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
/* incall music delivery mixer */
@@ -4834,6 +4892,9 @@ static const struct snd_kcontrol_new slimbus_6_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia25", MSM_BACKEND_DAI_SLIMBUS_6_RX,
MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new slimbus_7_rx_mixer_controls[] = {
@@ -4900,6 +4961,9 @@ static const struct snd_kcontrol_new slimbus_7_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia25", MSM_BACKEND_DAI_SLIMBUS_7_RX,
MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new usb_audio_rx_mixer_controls[] = {
@@ -4951,6 +5015,9 @@ static const struct snd_kcontrol_new usb_audio_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_USB_RX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
@@ -5011,6 +5078,9 @@ static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_BT_SCO_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = {
@@ -5062,6 +5132,9 @@ static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
@@ -5122,6 +5195,9 @@ static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_FM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = {
@@ -5182,6 +5258,9 @@ static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
@@ -5245,6 +5324,9 @@ static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
@@ -5308,6 +5390,9 @@ static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = {
@@ -5359,6 +5444,9 @@ static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = {
@@ -5410,6 +5498,9 @@ static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = {
@@ -5464,6 +5555,9 @@ static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_PRI_TDM_RX_0,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = {
@@ -5518,6 +5612,9 @@ static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_PRI_TDM_RX_1,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = {
@@ -5572,6 +5669,9 @@ static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_PRI_TDM_RX_2,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = {
@@ -5626,6 +5726,9 @@ static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_PRI_TDM_RX_3,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new pri_tdm_tx_0_mixer_controls[] = {
@@ -5731,6 +5834,9 @@ static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_TDM_RX_0,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = {
@@ -5785,6 +5891,9 @@ static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_TDM_RX_1,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = {
@@ -5839,6 +5948,9 @@ static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_TDM_RX_2,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = {
@@ -5893,6 +6005,9 @@ static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_TDM_RX_3,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_tdm_tx_0_mixer_controls[] = {
@@ -5998,6 +6113,9 @@ static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_0,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new tert_tdm_tx_0_mixer_controls[] = {
@@ -6103,6 +6221,9 @@ static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_1,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = {
@@ -6157,6 +6278,9 @@ static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_2,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = {
@@ -6211,6 +6335,9 @@ static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_3,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = {
@@ -6265,6 +6392,9 @@ static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_TERT_TDM_RX_4,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = {
@@ -6322,6 +6452,9 @@ static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = {
@@ -6430,6 +6563,9 @@ static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = {
@@ -6487,6 +6623,9 @@ static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = {
@@ -6544,6 +6683,9 @@ static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia26", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
@@ -7233,6 +7375,114 @@ static const struct snd_kcontrol_new mmul8_mixer_controls[] = {
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new mmul16_mixer_controls[] = {
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SLIM_6_TX", MSM_BACKEND_DAI_SLIMBUS_6_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_AUX_PCM_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new mmul9_mixer_controls[] = {
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
@@ -7479,6 +7729,18 @@ static const struct snd_kcontrol_new mmul21_mixer_controls[] = {
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new mmul27_mixer_controls[] = {
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
@@ -11586,6 +11848,7 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
SND_SOC_DAPM_AIF_IN("MM_DL23", "MultiMedia23 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("MM_DL24", "MultiMedia24 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("MM_DL25", "MultiMedia25 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL26", "MultiMedia26 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
@@ -11595,11 +11858,13 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL9", "MultiMedia9 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL16", "MultiMedia16 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL17", "MultiMedia17 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL18", "MultiMedia18 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL19", "MultiMedia19 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL20", "MultiMedia20 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL21", "MultiMedia21 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL27", "MultiMedia27 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VOICE2_DL", "Voice2 Playback", 0, 0, 0, 0),
@@ -11946,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),
@@ -11958,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",
@@ -12332,6 +12607,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia9 Mixer", SND_SOC_NOPM, 0, 0,
mmul9_mixer_controls, ARRAY_SIZE(mmul9_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia16 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul16_mixer_controls, ARRAY_SIZE(mmul16_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia17 Mixer", SND_SOC_NOPM, 0, 0,
mmul17_mixer_controls, ARRAY_SIZE(mmul17_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia18 Mixer", SND_SOC_NOPM, 0, 0,
@@ -12342,6 +12619,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
mmul20_mixer_controls, ARRAY_SIZE(mmul20_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia21 Mixer", SND_SOC_NOPM, 0, 0,
mmul21_mixer_controls, ARRAY_SIZE(mmul21_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia27 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul27_mixer_controls, ARRAY_SIZE(mmul27_mixer_controls)),
SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -12664,6 +12943,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
&ext_ec_ref_mux_ul8),
SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL9 MUX", SND_SOC_NOPM, 0, 0,
&ext_ec_ref_mux_ul9),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL16 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul16),
SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL17 MUX", SND_SOC_NOPM, 0, 0,
&ext_ec_ref_mux_ul17),
SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL18 MUX", SND_SOC_NOPM, 0, 0,
@@ -12689,6 +12970,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"PRI_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"PRI_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"PRI_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
{"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12707,6 +12989,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"SEC_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SEC_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"},
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12730,6 +13013,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia23", "MM_DL23"},
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia24", "MM_DL24"},
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia25", "MM_DL25"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
{"SLIMBUS_2_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12748,6 +13032,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SLIMBUS_2_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"SLIMBUS_2_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SLIMBUS_2_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SLIMBUS_2_RX", NULL, "SLIMBUS_2_RX Audio Mixer"},
{"SLIMBUS_5_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12766,6 +13051,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SLIMBUS_5_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"SLIMBUS_5_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SLIMBUS_5_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SLIMBUS_5_RX", NULL, "SLIMBUS_5_RX Audio Mixer"},
{"HDMI Mixer", "MultiMedia1", "MM_DL1"},
@@ -12789,6 +13075,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"HDMI Mixer", "MultiMedia23", "MM_DL23"},
{"HDMI Mixer", "MultiMedia24", "MM_DL24"},
{"HDMI Mixer", "MultiMedia25", "MM_DL25"},
+ {"HDMI Mixer", "MultiMedia26", "MM_DL26"},
{"HDMI", NULL, "HDMI Mixer"},
{"DISPLAY_PORT Mixer", "MultiMedia1", "MM_DL1"},
@@ -12807,6 +13094,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"DISPLAY_PORT Mixer", "MultiMedia14", "MM_DL14"},
{"DISPLAY_PORT Mixer", "MultiMedia15", "MM_DL15"},
{"DISPLAY_PORT Mixer", "MultiMedia16", "MM_DL16"},
+ {"DISPLAY_PORT Mixer", "MultiMedia26", "MM_DL26"},
{"DISPLAY_PORT", NULL, "DISPLAY_PORT Mixer"},
{"SPDIF_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12825,6 +13113,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SPDIF_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"SPDIF_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SPDIF_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SPDIF_RX", NULL, "SPDIF_RX Audio Mixer"},
/* incall */
@@ -12865,6 +13154,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SLIMBUS_6_RX Audio Mixer", "MultiMedia23", "MM_DL23"},
{"SLIMBUS_6_RX Audio Mixer", "MultiMedia24", "MM_DL24"},
{"SLIMBUS_6_RX Audio Mixer", "MultiMedia25", "MM_DL25"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Audio Mixer"},
{"SLIMBUS_7_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12888,6 +13178,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SLIMBUS_7_RX Audio Mixer", "MultiMedia23", "MM_DL23"},
{"SLIMBUS_7_RX Audio Mixer", "MultiMedia24", "MM_DL24"},
{"SLIMBUS_7_RX Audio Mixer", "MultiMedia25", "MM_DL25"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SLIMBUS_7_RX", NULL, "SLIMBUS_7_RX Audio Mixer"},
{"USB_AUDIO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12906,6 +13197,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"USB_AUDIO_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"USB_AUDIO_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"USB_AUDIO_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"USB_AUDIO_RX", NULL, "USB_AUDIO_RX Audio Mixer"},
{"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
@@ -12935,6 +13227,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia8 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
{"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia16 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia5 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
{"MultiMedia5 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12953,6 +13246,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12970,6 +13264,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Audio Mixer"},
{"TERT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -12986,6 +13281,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"TERT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"TERT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"TERT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Audio Mixer"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13002,6 +13298,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Audio Mixer"},
{"SEC_MI2S_RX_SD1 Audio Mixer", "MultiMedia6", "MM_DL6"},
@@ -13025,6 +13322,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"PRI_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"PRI_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"PRI_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Audio Mixer"},
{"INT0_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13042,6 +13340,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"INT0_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"INT0_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"INT0_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"INT0_MI2S_RX", NULL, "INT0_MI2S_RX Audio Mixer"},
{"INT4_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13059,6 +13358,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"INT4_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"INT4_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"INT4_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"INT4_MI2S_RX", NULL, "INT4_MI2S_RX Audio Mixer"},
{"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13078,6 +13378,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"QUIN_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"QUIN_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"QUIN_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
{"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX Audio Mixer"},
{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13097,6 +13398,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"},
{"PRI_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13116,6 +13418,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"PRI_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"PRI_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"PRI_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Audio Mixer"},
{"PRI_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13135,6 +13438,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"PRI_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"PRI_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"PRI_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Audio Mixer"},
{"PRI_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13154,6 +13458,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"PRI_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"PRI_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"PRI_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 Audio Mixer"},
{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13191,6 +13496,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"},
{"SEC_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13210,6 +13516,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SEC_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"SEC_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Audio Mixer"},
{"SEC_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13229,6 +13536,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SEC_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"SEC_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Audio Mixer"},
{"SEC_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13248,6 +13556,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SEC_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"SEC_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 Audio Mixer"},
{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13285,6 +13594,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Audio Mixer"},
{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13322,6 +13632,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Audio Mixer"},
{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13341,6 +13652,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Audio Mixer"},
{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13360,6 +13672,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Audio Mixer"},
{"TERT_TDM_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13379,6 +13692,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"TERT_TDM_RX_4 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"TERT_TDM_RX_4 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"TERT_TDM_RX_4 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"TERT_TDM_RX_4", NULL, "TERT_TDM_RX_4 Audio Mixer"},
{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13399,6 +13713,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia20", "MM_DL20"},
{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Audio Mixer"},
{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13417,6 +13732,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"},
{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13435,6 +13751,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"},
{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13473,6 +13790,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia20", "MM_DL20"},
{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Audio Mixer"},
{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13493,6 +13811,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia20", "MM_DL20"},
{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Audio Mixer"},
{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -13513,6 +13832,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia20", "MM_DL20"},
{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
{"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"},
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
@@ -13520,9 +13840,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia3 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"},
+ {"MultiMedia16 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"MultiMedia2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"MultiMedia6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia27 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"MultiMedia1 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"},
{"MultiMedia2 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"},
{"MultiMedia1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
@@ -13532,24 +13854,29 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
{"MultiMedia2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia27 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"MultiMedia3 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"},
{"MultiMedia5 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"MultiMedia1 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
{"MultiMedia3 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
{"MultiMedia5 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
+ {"MultiMedia16 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"},
+ {"MultiMedia16 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
{"MultiMedia1 Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"},
{"MultiMedia3 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"},
{"MultiMedia5 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"},
{"MultiMedia1 Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"},
{"MultiMedia3 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
{"MultiMedia5 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
+ {"MultiMedia16 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
{"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia2 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
{"MultiMedia2 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia2 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
{"MultiMedia1 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia27 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"MultiMedia2 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
{"MultiMedia6 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia6 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
@@ -13558,9 +13885,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia6 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
{"MultiMedia3 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
{"MultiMedia5 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+ {"MultiMedia16 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
{"MultiMedia6 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
{"MultiMedia3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
{"MultiMedia5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"MultiMedia16 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
{"MultiMedia6 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"MultiMedia6 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"MultiMedia6 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
@@ -13741,6 +14070,24 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia6 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
{"MultiMedia8 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+ {"MultiMedia16 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia16 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia16 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia16 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia16 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia16 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia16 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia16 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia16 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia16 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia16 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia16 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia16 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia16 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia16 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia16 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"MultiMedia16 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
@@ -13823,8 +14170,10 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia19 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"MultiMedia8 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia16 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia4 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia16 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia17 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia18 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia19 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
@@ -13840,6 +14189,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia19 Mixer", "AFE_PCM_TX", "PCM_TX"},
{"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"},
{"MultiMedia8 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia16 Mixer", "AFE_PCM_TX", "PCM_TX"},
{"MM_UL1", NULL, "MultiMedia1 Mixer"},
{"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MM_UL2", NULL, "MultiMedia2 Mixer"},
@@ -13849,11 +14199,13 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MM_UL6", NULL, "MultiMedia6 Mixer"},
{"MM_UL8", NULL, "MultiMedia8 Mixer"},
{"MM_UL9", NULL, "MultiMedia9 Mixer"},
+ {"MM_UL16", NULL, "MultiMedia16 Mixer"},
{"MM_UL17", NULL, "MultiMedia17 Mixer"},
{"MM_UL18", NULL, "MultiMedia18 Mixer"},
{"MM_UL19", NULL, "MultiMedia19 Mixer"},
{"MM_UL20", NULL, "MultiMedia20 Mixer"},
{"MM_UL21", NULL, "MultiMedia21 Mixer"},
+ {"MM_UL27", NULL, "MultiMedia27 Mixer"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
@@ -14266,6 +14618,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MM_UL6", NULL, "AUDIO_REF_EC_UL6 MUX"},
{"MM_UL8", NULL, "AUDIO_REF_EC_UL8 MUX"},
{"MM_UL9", NULL, "AUDIO_REF_EC_UL9 MUX"},
+ {"MM_UL16", NULL, "AUDIO_REF_EC_UL16 MUX"},
{"MM_UL17", NULL, "AUDIO_REF_EC_UL17 MUX"},
{"MM_UL18", NULL, "AUDIO_REF_EC_UL18 MUX"},
{"MM_UL19", NULL, "AUDIO_REF_EC_UL19 MUX"},
@@ -15142,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"},
@@ -15181,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" },
@@ -15316,10 +15674,6 @@ static int msm_pcm_routing_close(struct snd_pcm_substream *substream)
bedai->active = 0;
bedai->sample_rate = 0;
bedai->channel = 0;
- for (i = 0; i < MSM_FRONTEND_DAI_MAX; i++) {
- if (bedai->passthr_mode[i] != LISTEN)
- bedai->passthr_mode[i] = LEGACY_PCM;
- }
mutex_unlock(&routing_lock);
return 0;
@@ -15858,6 +16212,8 @@ int msm_routing_set_downmix_control_data(int be_id, int session_id,
char *adm_params = NULL;
int port_id, copp_idx = 0;
uint32_t params_length = 0;
+ uint16_t ii;
+ uint16_t *dst_gain_ptr = NULL;
if (be_id >= MSM_BACKEND_DAI_MAX) {
rc = -EINVAL;
@@ -15870,7 +16226,7 @@ int msm_routing_set_downmix_control_data(int be_id, int session_id,
variable_payload = dnmix_param->num_output_channels * sizeof(uint16_t)+
dnmix_param->num_input_channels * sizeof(uint16_t) +
- dnmix_param->num_output_channels * sizeof(uint16_t) *
+ dnmix_param->num_output_channels *
dnmix_param->num_input_channels * sizeof(uint16_t);
i = (variable_payload % sizeof(uint32_t));
variable_payload += (i == 0) ? 0 : sizeof(uint32_t) - i;
@@ -15909,14 +16265,15 @@ int msm_routing_set_downmix_control_data(int be_id, int session_id,
dnmix_param->num_output_channels * sizeof(uint16_t)),
dnmix_param->input_channel_map,
dnmix_param->num_input_channels * sizeof(uint16_t));
- memcpy(((u8 *)adm_params +
+
+ dst_gain_ptr = (uint16_t *) ((u8 *)adm_params +
sizeof(struct adm_pspd_param_data_t) +
sizeof(struct audproc_chmixer_param_coeff) +
(dnmix_param->num_output_channels * sizeof(uint16_t)) +
- (dnmix_param->num_input_channels * sizeof(uint16_t))),
- dnmix_param->gain,
- (dnmix_param->num_output_channels * sizeof(uint16_t)) *
(dnmix_param->num_input_channels * sizeof(uint16_t)));
+ for (ii = 0; ii < dnmix_param->num_output_channels *
+ dnmix_param->num_input_channels; ii++)
+ dst_gain_ptr[ii] = (uint16_t) dnmix_param->gain[ii];
if (params_length) {
rc = adm_set_pspd_matrix_params(port_id,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index a8daff09db58..21dfa48308c3 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -56,8 +56,8 @@
#define LPASS_BE_SEC_MI2S_TX "SEC_MI2S_TX"
#define LPASS_BE_PRI_MI2S_RX "PRI_MI2S_RX"
#define LPASS_BE_PRI_MI2S_TX "PRI_MI2S_TX"
-#define LPASS_BE_TERT_MI2S_RX "TERTIARY_MI2S_RX"
-#define LPASS_BE_TERT_MI2S_TX "TERTIARY_MI2S_TX"
+#define LPASS_BE_TERT_MI2S_RX "TERT_MI2S_RX"
+#define LPASS_BE_TERT_MI2S_TX "TERT_MI2S_TX"
#define LPASS_BE_AUDIO_I2S_RX "AUDIO_I2S_RX"
#define LPASS_BE_STUB_RX "STUB_RX"
#define LPASS_BE_STUB_TX "STUB_TX"
@@ -198,6 +198,8 @@ enum {
MSM_FRONTEND_DAI_MULTIMEDIA23,
MSM_FRONTEND_DAI_MULTIMEDIA24,
MSM_FRONTEND_DAI_MULTIMEDIA25,
+ MSM_FRONTEND_DAI_MULTIMEDIA26,
+ MSM_FRONTEND_DAI_MULTIMEDIA27,
MSM_FRONTEND_DAI_CS_VOICE,
MSM_FRONTEND_DAI_VOIP,
MSM_FRONTEND_DAI_AFE_RX,
@@ -223,8 +225,8 @@ enum {
MSM_FRONTEND_DAI_MAX,
};
-#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA25 + 1)
-#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA25
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA27 + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA27
enum {
MSM_BACKEND_DAI_PRI_I2S_RX = 0,
diff --git a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c
index 5c5f7bc482c8..18cac3424054 100644
--- a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c
@@ -27,6 +27,8 @@
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/q6asm-v2.h>
+#include <sound/q6core.h>
+#include <sound/q6audio-v2.h>
#include <sound/pcm_params.h>
#include <sound/timer.h>
#include <sound/tlv.h>
@@ -40,11 +42,21 @@
#include "msm-qti-pp-config.h"
#define LOOPBACK_SESSION_MAX_NUM_STREAMS 2
+/* Max volume corresponding to 24dB */
+#define TRANSCODE_LR_VOL_MAX_STEPS 0xFFFF
+
+#define APP_TYPE_CONFIG_IDX_APP_TYPE 0
+#define APP_TYPE_CONFIG_IDX_ACDB_ID 1
+#define APP_TYPE_CONFIG_IDX_SAMPLE_RATE 2
+#define APP_TYPE_CONFIG_IDX_BE_ID 3
static DEFINE_MUTEX(transcode_loopback_session_lock);
struct trans_loopback_pdata {
struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX];
+ int32_t ion_fd[MSM_FRONTEND_DAI_MAX];
+ uint32_t master_gain;
+ int perf_mode;
};
struct loopback_stream {
@@ -79,6 +91,11 @@ struct msm_transcode_loopback {
int session_id;
struct audio_client *audio_client;
+ int32_t shm_ion_fd;
+ struct ion_client *lib_ion_client;
+ struct ion_client *shm_ion_client;
+ struct ion_handle *lib_ion_handle;
+ struct ion_handle *shm_ion_handle;
};
/* Transcode loopback global info struct */
@@ -179,6 +196,65 @@ static void populate_codec_list(struct msm_transcode_loopback *trans,
}
}
+static int msm_transcode_map_ion_fd(struct msm_transcode_loopback *trans,
+ int fd)
+{
+ ion_phys_addr_t paddr;
+ size_t pa_len = 0;
+ int ret = 0;
+
+ ret = msm_audio_ion_phys_assign("audio_lib_mem_client",
+ &trans->lib_ion_client,
+ &trans->lib_ion_handle, fd,
+ &paddr, &pa_len, HLOS_TO_ADSP);
+ if (ret) {
+ pr_err("%s: audio lib ION phys failed, rc = %d\n", __func__,
+ ret);
+ goto done;
+ }
+
+ ret = q6core_add_remove_pool_pages(paddr, pa_len,
+ ADSP_MEMORY_MAP_HLOS_PHYSPOOL, true);
+ if (ret) {
+ pr_err("%s: add pages failed, rc = %d\n", __func__, ret);
+ /* Assign back to HLOS if add pages cmd failed */
+ msm_audio_ion_phys_free(trans->lib_ion_client,
+ trans->lib_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ }
+
+done:
+ return ret;
+}
+
+static int msm_transcode_unmap_ion_fd(struct msm_transcode_loopback *trans)
+{
+ ion_phys_addr_t paddr;
+ size_t pa_len = 0;
+ int ret = 0;
+
+ if (!trans->lib_ion_client || !trans->lib_ion_handle) {
+ pr_err("%s: ion_client or ion_handle is NULL", __func__);
+ return -EINVAL;
+ }
+ ret = msm_audio_ion_phys_free(trans->lib_ion_client,
+ trans->lib_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ if (ret) {
+ pr_err("%s: audio lib ION phys failed, rc = %d\n", __func__,
+ ret);
+ goto done;
+ }
+
+ ret = q6core_add_remove_pool_pages(paddr, pa_len,
+ ADSP_MEMORY_MAP_HLOS_PHYSPOOL, false);
+ if (ret)
+ pr_err("%s: remove pages failed, rc = %d\n", __func__, ret);
+
+done:
+ return ret;
+}
+
static int msm_transcode_loopback_open(struct snd_compr_stream *cstream)
{
int ret = 0;
@@ -224,6 +300,12 @@ static int msm_transcode_loopback_open(struct snd_compr_stream *cstream)
goto exit;
}
msm_adsp_init_mixer_ctl_pp_event_queue(rtd);
+ if (pdata->ion_fd[rtd->dai_link->be_id] > 0) {
+ ret = msm_transcode_map_ion_fd(trans,
+ pdata->ion_fd[rtd->dai_link->be_id]);
+ if (ret < 0)
+ goto exit;
+ }
}
pr_debug("%s: num stream%d, stream name %s\n", __func__,
@@ -274,7 +356,11 @@ static int msm_transcode_loopback_free(struct snd_compr_stream *cstream)
struct snd_compr_runtime *runtime = cstream->runtime;
struct msm_transcode_loopback *trans = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(cstream);
+ struct trans_loopback_pdata *pdata = snd_soc_platform_get_drvdata(
+ rtd->platform);
int ret = 0;
+ ion_phys_addr_t paddr;
+ size_t pa_len = 0;
mutex_lock(&trans->lock);
@@ -286,6 +372,16 @@ static int msm_transcode_loopback_free(struct snd_compr_stream *cstream)
if (cstream->direction == SND_COMPRESS_PLAYBACK) {
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_free(trans->shm_ion_client,
+ trans->shm_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ trans->shm_ion_fd = 0;
+ }
+ if (pdata->ion_fd[rtd->dai_link->be_id] > 0) {
+ msm_transcode_unmap_ion_fd(trans);
+ pdata->ion_fd[rtd->dai_link->be_id] = 0;
+ }
} else if (cstream->direction == SND_COMPRESS_CAPTURE) {
memset(&trans->source, 0, sizeof(struct loopback_stream));
}
@@ -337,6 +433,8 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream,
struct msm_transcode_loopback *trans = runtime->private_data;
struct snd_soc_pcm_runtime *soc_pcm_rx;
struct snd_soc_pcm_runtime *soc_pcm_tx;
+ struct snd_soc_pcm_runtime *rtd;
+ struct trans_loopback_pdata *pdata;
uint32_t bit_width = 16;
int ret = 0;
@@ -347,6 +445,9 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream,
mutex_lock(&trans->lock);
+ rtd = snd_pcm_substream_chip(cstream);
+ pdata = snd_soc_platform_get_drvdata(rtd->platform);
+
if (cstream->direction == SND_COMPRESS_PLAYBACK) {
if (codec_param->codec.id == SND_AUDIOCODEC_PCM) {
trans->sink.codec_format =
@@ -428,7 +529,7 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream,
pr_debug("%s: ASM client allocated, callback %pK\n", __func__,
loopback_event_handler);
trans->session_id = trans->audio_client->session;
- trans->audio_client->perf_mode = false;
+ trans->audio_client->perf_mode = pdata->perf_mode;
ret = q6asm_open_transcode_loopback(trans->audio_client,
bit_width,
trans->source.codec_format,
@@ -447,7 +548,7 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream,
if (trans->source.codec_format != FORMAT_LINEAR_PCM)
msm_pcm_routing_reg_phy_compr_stream(
soc_pcm_tx->dai_link->be_id,
- trans->audio_client->perf_mode,
+ false,
trans->session_id,
SNDRV_PCM_STREAM_CAPTURE,
COMPRESSED_PASSTHROUGH_GEN);
@@ -460,7 +561,7 @@ static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream,
/* Opening Rx ADM in LOW_LATENCY mode by default */
msm_pcm_routing_reg_phy_stream(
soc_pcm_rx->dai_link->be_id,
- true,
+ trans->audio_client->perf_mode,
trans->session_id,
SNDRV_PCM_STREAM_PLAYBACK);
pr_debug("%s: Successfully opened ADM sessions\n", __func__);
@@ -493,6 +594,46 @@ static int msm_transcode_loopback_get_caps(struct snd_compr_stream *cstream,
return 0;
}
+static int msm_transcode_loopback_set_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct trans_loopback_pdata *pdata;
+
+ if (!metadata || !cstream) {
+ pr_err("%s: Invalid arguments\n", __func__);
+ return -EINVAL;
+ }
+
+ rtd = snd_pcm_substream_chip(cstream);
+ pdata = snd_soc_platform_get_drvdata(rtd->platform);
+
+ switch (metadata->key) {
+ case SNDRV_COMPRESS_LATENCY_MODE:
+ {
+ switch (metadata->value[0]) {
+ case SNDRV_COMPRESS_LEGACY_LATENCY_MODE:
+ pdata->perf_mode = LEGACY_PCM_MODE;
+ break;
+ case SNDRV_COMPRESS_LOW_LATENCY_MODE:
+ pdata->perf_mode = LOW_LATENCY_PCM_MODE;
+ break;
+ default:
+ pr_debug("%s: Unsupported latency mode %d, default to Legacy\n",
+ __func__, metadata->value[0]);
+ pdata->perf_mode = LEGACY_PCM_MODE;
+ break;
+ }
+ }
+ break;
+ default:
+ pr_debug("%s: Unsupported metadata %d\n",
+ __func__, metadata->key);
+ break;
+ }
+ return 0;
+}
+
static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -557,7 +698,7 @@ done:
return ret;
}
-static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol,
+static int msm_transcode_shm_ion_fd_map_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
@@ -566,7 +707,6 @@ static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol,
snd_soc_component_get_drvdata(comp);
struct snd_compr_stream *cstream = NULL;
struct msm_transcode_loopback *prtd;
- int fd;
int ret = 0;
if (fe_id >= MSM_FRONTEND_DAI_MAX) {
@@ -596,10 +736,36 @@ static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol,
goto done;
}
- memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd));
- ret = q6asm_send_ion_fd(prtd->audio_client, fd);
+ memcpy(&prtd->shm_ion_fd, ucontrol->value.bytes.data,
+ sizeof(prtd->shm_ion_fd));
+ ret = q6asm_audio_map_shm_fd(prtd->audio_client,
+ &prtd->shm_ion_client,
+ &prtd->shm_ion_handle, prtd->shm_ion_fd);
if (ret < 0)
- pr_err("%s: failed to register ion fd\n", __func__);
+ pr_err("%s: failed to map shm mem\n", __func__);
+done:
+ return ret;
+}
+
+
+static int msm_transcode_lib_ion_fd_map_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ int ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&pdata->ion_fd[fe_id], ucontrol->value.bytes.data,
+ sizeof(pdata->ion_fd[fe_id]));
done:
return ret;
}
@@ -663,6 +829,141 @@ done:
return ret;
}
+static int msm_transcode_playback_app_type_cfg_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[
+ APP_TYPE_CONFIG_IDX_APP_TYPE];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[
+ APP_TYPE_CONFIG_IDX_ACDB_ID];
+ if (ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[
+ APP_TYPE_CONFIG_IDX_SAMPLE_RATE];
+ pr_debug("%s: fe_id %llu session_type %d be_id %d app_type %d acdb_dev_id %d sample_rate- %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_transcode_playback_stream_app_type_cfg set failed returned %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_transcode_playback_app_type_cfg_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_transcode_playback_stream_app_type_cfg get failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_APP_TYPE] =
+ cfg_data.app_type;
+ ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_ACDB_ID] =
+ cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] =
+ cfg_data.sample_rate;
+ ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID] = be_id;
+ pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+done:
+ return ret;
+}
+
+static int msm_transcode_set_volume(struct snd_compr_stream *cstream,
+ uint32_t master_gain)
+{
+ int rc = 0;
+ struct msm_transcode_loopback *prtd;
+ struct snd_soc_pcm_runtime *rtd;
+
+ pr_debug("%s: master_gain %d\n", __func__, master_gain);
+ if (!cstream || !cstream->runtime) {
+ pr_err("%s: session not active\n", __func__);
+ return -EPERM;
+ }
+ rtd = cstream->private_data;
+ prtd = cstream->runtime->private_data;
+
+ if (!rtd || !rtd->platform || !prtd || !prtd->audio_client) {
+ pr_err("%s: invalid rtd, prtd or audio client", __func__);
+ return -EINVAL;
+ }
+
+ rc = q6asm_set_volume(prtd->audio_client, master_gain);
+ if (rc < 0)
+ pr_err("%s: Send vol gain command failed rc=%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static int msm_transcode_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ uint32_t ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ pdata->master_gain = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: fe_id %lu master_gain %d\n",
+ __func__, fe_id, pdata->master_gain);
+ if (cstream)
+ ret = msm_transcode_set_volume(cstream, pdata->master_gain);
+ return ret;
+}
+
+static int msm_transcode_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+
+ struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
+ snd_soc_component_get_drvdata(comp);
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: fe_id %lu\n", __func__, fe_id);
+ ucontrol->value.integer.value[0] = pdata->master_gain;
+
+ return 0;
+}
+
static int msm_transcode_stream_cmd_control(
struct snd_soc_pcm_runtime *rtd)
{
@@ -773,7 +1074,8 @@ done:
return ret;
}
-static int msm_transcode_add_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
+static int msm_transcode_add_shm_ion_fd_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
{
const char *mixer_ctl_name = "Playback ION FD";
const char *deviceNo = "NN";
@@ -785,7 +1087,53 @@ static int msm_transcode_add_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
.name = "?",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = msm_adsp_stream_cmd_info,
- .put = msm_transcode_ion_fd_map_put,
+ .put = msm_transcode_shm_ion_fd_map_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_ion_fd_config_control[0].name = mixer_str;
+ fe_ion_fd_config_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_ion_fd_config_control,
+ ARRAY_SIZE(fe_ion_fd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_transcode_add_lib_ion_fd_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback ION LIB FD";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_ion_fd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_transcode_lib_ion_fd_map_put,
.private_value = 0,
}
};
@@ -864,6 +1212,99 @@ done:
return ret;
}
+static int msm_transcode_app_type_cfg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 5;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_transcode_add_app_type_cfg_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ char mixer_str[32];
+ int rc = 0;
+ struct snd_kcontrol_new fe_app_type_cfg_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_transcode_app_type_cfg_info,
+ .put = msm_transcode_playback_app_type_cfg_put,
+ .get = msm_transcode_playback_app_type_cfg_get,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+
+ return -EINVAL;
+ }
+
+ if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
+
+ snprintf(mixer_str, sizeof(mixer_str),
+ "Audio Stream %d App Type Cfg",
+ rtd->pcm->device);
+
+ fe_app_type_cfg_control[0].name = mixer_str;
+ fe_app_type_cfg_control[0].private_value = rtd->dai_link->be_id;
+
+ fe_app_type_cfg_control[0].put =
+ msm_transcode_playback_app_type_cfg_put;
+ fe_app_type_cfg_control[0].get =
+ msm_transcode_playback_app_type_cfg_get;
+
+ pr_debug("Registering new mixer ctl %s", mixer_str);
+ snd_soc_add_platform_controls(rtd->platform,
+ fe_app_type_cfg_control,
+ ARRAY_SIZE(fe_app_type_cfg_control));
+ }
+
+ return rc;
+}
+static int msm_transcode_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = TRANSCODE_LR_VOL_MAX_STEPS;
+ return 0;
+}
+
+static int msm_transcode_add_volume_control(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_kcontrol_new fe_volume_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Transcode Loopback Rx Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_transcode_volume_info,
+ .get = msm_transcode_volume_get,
+ .put = msm_transcode_volume_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return -EINVAL;
+ }
+ if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
+ fe_volume_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s",
+ fe_volume_control[0].name);
+ snd_soc_add_platform_controls(rtd->platform, fe_volume_control,
+ ARRAY_SIZE(fe_volume_control));
+ }
+ return 0;
+}
+
static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd)
{
int rc;
@@ -877,9 +1318,14 @@ static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd)
pr_err("%s: ADSP Stream callback Control open failed\n",
__func__);
- rc = msm_transcode_add_ion_fd_cmd_control(rtd);
+ rc = msm_transcode_add_shm_ion_fd_cmd_control(rtd);
if (rc)
- pr_err("%s: Could not add transcode ion fd Control\n",
+ pr_err("%s: Could not add transcode shm ion fd Control\n",
+ __func__);
+
+ rc = msm_transcode_add_lib_ion_fd_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add transcode lib ion fd Control\n",
__func__);
rc = msm_transcode_add_event_ack_cmd_control(rtd);
@@ -887,6 +1333,16 @@ static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd)
pr_err("%s: Could not add transcode event ack Control\n",
__func__);
+ rc = msm_transcode_add_app_type_cfg_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr App Type Cfg Control\n",
+ __func__);
+
+ rc = msm_transcode_add_volume_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add transcode volume Control\n",
+ __func__);
+
return 0;
}
@@ -896,6 +1352,7 @@ static struct snd_compr_ops msm_transcode_loopback_ops = {
.trigger = msm_transcode_loopback_trigger,
.set_params = msm_transcode_loopback_set_params,
.get_caps = msm_transcode_loopback_get_caps,
+ .set_metadata = msm_transcode_loopback_set_metadata,
};
@@ -910,6 +1367,7 @@ static int msm_transcode_loopback_probe(struct snd_soc_platform *platform)
if (!pdata)
return -ENOMEM;
+ pdata->perf_mode = LOW_LATENCY_PCM_MODE;
snd_soc_platform_set_drvdata(platform, pdata);
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index e1bfc950d0e3..018681309f2e 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -2364,7 +2364,8 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
struct adm_cmd_device_open_v5 open;
struct adm_cmd_device_open_v6 open_v6;
int ret = 0;
- int port_idx, copp_idx, flags;
+ int port_idx, flags;
+ int copp_idx = -1;
int tmp_port = q6audio_get_port_id(port_id);
pr_debug("%s:port %#x path:%d rate:%d mode:%d perf_mode:%d,topo_id %d\n",
@@ -2418,8 +2419,17 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
(topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY))
rate = 16000;
- copp_idx = adm_get_idx_if_copp_exists(port_idx, topology, perf_mode,
- rate, bit_width, app_type);
+ /*
+ * Routing driver reuses the same adm for streams with the same
+ * app_type, sample_rate etc.
+ * This isn't allowed for ULL streams as per the DSP interface
+ */
+ if (perf_mode != ULTRA_LOW_LATENCY_PCM_MODE)
+ copp_idx = adm_get_idx_if_copp_exists(port_idx, topology,
+ perf_mode,
+ rate, bit_width,
+ app_type);
+
if (copp_idx < 0) {
copp_idx = adm_get_next_available_copp(port_idx);
if (copp_idx >= MAX_COPPS_PER_PORT) {
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index a44569530846..14f9411104b3 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -36,6 +36,7 @@
#include <sound/apr_audio-v2.h>
#include <sound/q6asm-v2.h>
+#include <sound/q6core.h>
#include <sound/q6audio-v2.h>
#include <sound/audio_cal_utils.h>
#include <sound/adsp_err.h>
@@ -7117,13 +7118,11 @@ fail_cmd:
return rc;
}
-int q6asm_send_ion_fd(struct audio_client *ac, int fd)
+int q6asm_audio_map_shm_fd(struct audio_client *ac, struct ion_client **client,
+ struct ion_handle **handle, int fd)
{
- struct ion_client *client;
- struct ion_handle *handle;
ion_phys_addr_t paddr;
size_t pa_len = 0;
- void *vaddr;
int ret;
int sz = 0;
struct avs_rtic_shared_mem_addr shm;
@@ -7139,19 +7138,11 @@ int q6asm_send_ion_fd(struct audio_client *ac, int fd)
goto fail_cmd;
}
- ret = msm_audio_ion_import("audio_mem_client",
- &client,
- &handle,
- fd,
- NULL,
- 0,
- &paddr,
- &pa_len,
- &vaddr);
+ ret = msm_audio_ion_phys_assign("audio_shm_mem_client", client,
+ handle, fd,
+ &paddr, &pa_len, HLOS_TO_ADSP);
if (ret) {
- pr_err("%s: audio ION import failed, rc = %d\n",
- __func__, ret);
- ret = -ENOMEM;
+ pr_err("%s: shm ION phys failed, rc = %d\n", __func__, ret);
goto fail_cmd;
}
/* get payload length */
@@ -7554,7 +7545,9 @@ int q6asm_set_mfc_panning_params(struct audio_client *ac,
struct asm_stream_param_data_v2 data;
struct audproc_chmixer_param_coeff pan_cfg;
uint16_t variable_payload = 0;
- uint16_t *asm_params = NULL;
+ char *asm_params = NULL;
+ uint16_t ii;
+ uint16_t *dst_gain_ptr = NULL;
sz = rc = i = 0;
if (ac == NULL) {
@@ -7615,7 +7608,7 @@ int q6asm_set_mfc_panning_params(struct audio_client *ac,
variable_payload = pan_param->num_output_channels * sizeof(uint16_t)+
pan_param->num_input_channels * sizeof(uint16_t) +
- pan_param->num_output_channels * sizeof(uint16_t) *
+ pan_param->num_output_channels *
pan_param->num_input_channels * sizeof(uint16_t);
i = (variable_payload % sizeof(uint32_t));
variable_payload += (i == 0) ? 0 : sizeof(uint32_t) - i;
@@ -7675,15 +7668,16 @@ int q6asm_set_mfc_panning_params(struct audio_client *ac,
pan_param->num_output_channels * sizeof(uint16_t)),
pan_param->input_channel_map,
pan_param->num_input_channels * sizeof(uint16_t));
- memcpy(((u8 *)asm_params + sizeof(struct apr_hdr) +
+
+ dst_gain_ptr = (uint16_t *) ((u8 *)asm_params + sizeof(struct apr_hdr) +
sizeof(struct asm_stream_cmd_set_pp_params_v2) +
sizeof(struct asm_stream_param_data_v2) +
sizeof(struct audproc_chmixer_param_coeff) +
(pan_param->num_output_channels * sizeof(uint16_t)) +
- (pan_param->num_input_channels * sizeof(uint16_t))),
- pan_param->gain,
- (pan_param->num_output_channels * sizeof(uint16_t)) *
(pan_param->num_input_channels * sizeof(uint16_t)));
+ for (ii = 0; ii < pan_param->num_output_channels *
+ pan_param->num_input_channels; ii++)
+ dst_gain_ptr[ii] = (uint16_t) pan_param->gain[ii];
rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params);
if (rc < 0) {
diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c
index 4340d31c218c..6fed443186e5 100644
--- a/sound/soc/msm/qdsp6v2/q6core.c
+++ b/sound/soc/msm/qdsp6v2/q6core.c
@@ -119,6 +119,18 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv)
q6core_lcl.bus_bw_resp_received = 1;
wake_up(&q6core_lcl.bus_bw_req_wait);
break;
+ case AVCS_CMD_ADD_POOL_PAGES:
+ pr_debug("%s: Cmd = AVCS_CMD_ADD_POOL_PAGES status[0x%x]\n",
+ __func__, payload1[1]);
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ case AVCS_CMD_REMOVE_POOL_PAGES:
+ pr_debug("%s: Cmd = AVCS_CMD_REMOVE_POOL_PAGES status[0x%x]\n",
+ __func__, payload1[1]);
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
default:
pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n",
__func__,
@@ -542,6 +554,56 @@ done:
return ret;
}
+int q6core_add_remove_pool_pages(ion_phys_addr_t buf_add, uint32_t bufsz,
+ uint32_t mempool_id, bool add_pages)
+{
+ struct avs_mem_assign_region mem_pool;
+ int ret = 0, sz;
+
+ if (add_pages)
+ mem_pool.hdr.opcode = AVCS_CMD_ADD_POOL_PAGES;
+ else
+ mem_pool.hdr.opcode = AVCS_CMD_REMOVE_POOL_PAGES;
+
+ /* get payload length */
+ sz = sizeof(struct avs_mem_assign_region);
+ mem_pool.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(sizeof(struct apr_hdr)),
+ APR_PKT_VER);
+ mem_pool.hdr.src_port = 0;
+ mem_pool.hdr.dest_port = 0;
+ mem_pool.hdr.token = 0;
+ mem_pool.hdr.pkt_size = sz;
+ mem_pool.pool_id = mempool_id;
+ mem_pool.size = bufsz;
+ mem_pool.addr_lsw = lower_32_bits(buf_add);
+ mem_pool.addr_msw = msm_audio_populate_upper_32_bits(buf_add);
+ pr_debug("%s: sending memory map, size %d\n",
+ __func__, bufsz);
+
+ q6core_lcl.bus_bw_resp_received = 0;
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&mem_pool);
+ if (ret < 0) {
+ pr_err("%s: library map region failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+ (q6core_lcl.bus_bw_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: timeout. waited for library memory map\n",
+ __func__);
+ ret = -ETIME;
+ goto done;
+ }
+ ret = 0;
+done:
+ return ret;
+}
+
static int q6core_dereg_all_custom_topologies(void)
{
int ret = 0;
diff --git a/sound/soc/msm/sdm660-ext-dai-links.c b/sound/soc/msm/sdm660-ext-dai-links.c
index 1c03d8c9e797..30c3ffe2347d 100644
--- a/sound/soc/msm/sdm660-ext-dai-links.c
+++ b/sound/soc/msm/sdm660-ext-dai-links.c
@@ -1270,10 +1270,10 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = {
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
},
{/* hw:x,33 */
- .name = MSM_DAILINK_NAME(Compress9),
- .stream_name = "Compress9",
+ .name = MSM_DAILINK_NAME(ULL_NOIRQ_2),
+ .stream_name = "MM_NOIRQ_2",
.cpu_dai_name = "MultiMedia16",
- .platform_name = "msm-compress-dsp",
+ .platform_name = "msm-pcm-dsp-noirq",
.dynamic = 1,
.dpcm_capture = 1,
.dpcm_playback = 1,
diff --git a/sound/soc/msm/sdm660-internal.c b/sound/soc/msm/sdm660-internal.c
index f4219148e81c..948fb287023d 100644
--- a/sound/soc/msm/sdm660-internal.c
+++ b/sound/soc/msm/sdm660-internal.c
@@ -1291,6 +1291,9 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_ignore_suspend(dapm, "AMIC1");
snd_soc_dapm_ignore_suspend(dapm, "AMIC2");
snd_soc_dapm_ignore_suspend(dapm, "AMIC3");
+ snd_soc_dapm_sync(dapm);
+
+ dapm = snd_soc_codec_get_dapm(dig_cdc);
snd_soc_dapm_ignore_suspend(dapm, "DMIC1");
snd_soc_dapm_ignore_suspend(dapm, "DMIC2");
snd_soc_dapm_ignore_suspend(dapm, "DMIC3");
@@ -2188,10 +2191,10 @@ static struct snd_soc_dai_link msm_int_dai[] = {
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
},
{/* hw:x,33 */
- .name = MSM_DAILINK_NAME(Compress9),
- .stream_name = "Compress9",
+ .name = MSM_DAILINK_NAME(ULL_NOIRQ_2),
+ .stream_name = "MM_NOIRQ_2",
.cpu_dai_name = "MultiMedia16",
- .platform_name = "msm-compress-dsp",
+ .platform_name = "msm-pcm-dsp-noirq",
.dynamic = 1,
.dpcm_capture = 1,
.dpcm_playback = 1,
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 2a5b3a293cd2..b123734f9fbd 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -437,7 +437,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node;
u32 ckr, rbgx, rbga, rbgb;
- u32 rate, req_rate, div;
+ u32 rate, req_rate = 0, div;
uint32_t count = 0;
unsigned long req_48kHz_rate, req_441kHz_rate;
int i;
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index deed48ef28b8..362446c36c9e 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -192,19 +192,16 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io;
struct rsnd_dai *rdai;
- int i, j;
-
- for_each_rsnd_dai(rdai, priv, j) {
+ int i;
- for (i = 0; i < RSND_MOD_MAX; i++) {
- io = &rdai->playback;
- if (mod == io->mod[i])
- callback(mod, io);
+ for_each_rsnd_dai(rdai, priv, i) {
+ io = &rdai->playback;
+ if (mod == io->mod[mod->type])
+ callback(mod, io);
- io = &rdai->capture;
- if (mod == io->mod[i])
- callback(mod, io);
- }
+ io = &rdai->capture;
+ if (mod == io->mod[mod->type])
+ callback(mod, io);
}
}
@@ -1019,7 +1016,7 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
}
}
- if (change)
+ if (change && cfg->update)
cfg->update(cfg->io, mod);
return change;
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 68b439ed22d7..460d29cbaaa5 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -691,13 +691,27 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod)
{
rsnd_src_irq_disable_gen2(mod);
- rsnd_mod_write(mod, SRC_CTRL, 0);
+ /*
+ * stop SRC output only
+ * see rsnd_src_quit_gen2
+ */
+ rsnd_mod_write(mod, SRC_CTRL, 0x01);
rsnd_src_error_record_gen2(mod);
return rsnd_src_stop(mod);
}
+static int rsnd_src_quit_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ /* stop both out/in */
+ rsnd_mod_write(mod, SRC_CTRL, 0);
+
+ return 0;
+}
+
static void __rsnd_src_interrupt_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
@@ -971,7 +985,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = {
.probe = rsnd_src_probe_gen2,
.remove = rsnd_src_remove_gen2,
.init = rsnd_src_init_gen2,
- .quit = rsnd_src_quit,
+ .quit = rsnd_src_quit_gen2,
.start = rsnd_src_start_gen2,
.stop = rsnd_src_stop_gen2,
.hw_params = rsnd_src_hw_params,
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 1427ec21bd7e..c62a2947ac14 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -39,6 +39,7 @@
#define SCKP (1 << 13) /* Serial Bit Clock Polarity */
#define SWSP (1 << 12) /* Serial WS Polarity */
#define SDTA (1 << 10) /* Serial Data Alignment */
+#define PDTA (1 << 9) /* Parallel Data Alignment */
#define DEL (1 << 8) /* Serial Data Delay */
#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */
#define TRMD (1 << 1) /* Transmit/Receive Mode Select */
@@ -286,7 +287,7 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 cr;
- cr = FORCE;
+ cr = FORCE | PDTA;
/*
* always use 32bit system word for easy clock calculation.
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e63b4346e82e..fae57d18bbd7 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -3063,7 +3063,8 @@ static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm,
*/
void snd_soc_card_change_online_state(struct snd_soc_card *soc_card, int online)
{
- snd_card_change_online_state(soc_card->snd_card, online);
+ if (soc_card && soc_card->snd_card)
+ snd_card_change_online_state(soc_card->snd_card, online);
}
EXPORT_SYMBOL(snd_soc_card_change_online_state);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 0ba9dfb854b3..9694fe13021f 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -203,11 +203,14 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n",
be->dai_link->name, event, dir);
- if (event == SND_SOC_DAPM_STREAM_STOP && be->dpcm[dir].users >= 1) {
+
+ if ((event == SND_SOC_DAPM_STREAM_STOP) &&
+ (be->dpcm[dir].users >= 1)) {
pr_debug("%s Don't close BE \n", __func__);
continue;
}
- snd_soc_dapm_stream_event(be, dir, event);
+
+ snd_soc_dapm_stream_event(be, dir, event);
}
snd_soc_dapm_stream_event(fe, dir, event);
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 2bba7dbc35f9..61f64dcd5374 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -384,6 +384,9 @@ static void snd_complete_urb(struct urb *urb)
if (unlikely(atomic_read(&ep->chip->shutdown)))
goto exit_clear;
+ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+ goto exit_clear;
+
if (usb_pipeout(ep->pipe)) {
retire_outbound_urb(ep, ctx);
/* can be stopped during retire callback */
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index d5cc315a5eb4..ad139d45f5b2 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -553,6 +553,8 @@ int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
if (size < sizeof(scale))
return -ENOMEM;
+ if (cval->min_mute)
+ scale[0] = SNDRV_CTL_TLVT_DB_MINMAX_MUTE;
scale[2] = cval->dBmin;
scale[3] = cval->dBmax;
if (copy_to_user(_tlv, scale, sizeof(scale)))
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 3417ef347e40..2b4b067646ab 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -64,6 +64,7 @@ struct usb_mixer_elem_info {
int cached;
int cache_val[MAX_CHANNELS];
u8 initialized;
+ u8 min_mute;
void *private_data;
};
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 04991b009132..5d2fc5f58bfe 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -1873,6 +1873,12 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
if (unitid == 7 && cval->control == UAC_FU_VOLUME)
snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
break;
+ /* lowest playback value is muted on C-Media devices */
+ case USB_ID(0x0d8c, 0x000c):
+ case USB_ID(0x0d8c, 0x0014):
+ if (strstr(kctl->id.name, "Playback"))
+ cval->min_mute = 1;
+ break;
}
}
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 29f38e2b4ca9..1cc20d138dae 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1143,6 +1143,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */
case USB_ID(0x05A3, 0x9420): /* ELP HD USB Camera */
case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
+ case USB_ID(0x1395, 0x740a): /* Sennheiser DECT */
case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */
case USB_ID(0x1de7, 0x0013): /* Phoenix Audio MT202exe */
case USB_ID(0x1de7, 0x0014): /* Phoenix Audio TMX320 */
diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h
index fa7208a32d76..8a679b21f0c4 100644
--- a/tools/include/linux/compiler.h
+++ b/tools/include/linux/compiler.h
@@ -115,4 +115,13 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
#define WRITE_ONCE(x, val) \
({ union { typeof(x) __val; char __c[1]; } __u = { .__val = (val) }; __write_once_size(&(x), __u.__c, sizeof(x)); __u.__val; })
+
+#ifndef __fallthrough
+# if defined(__GNUC__) && __GNUC__ >= 7
+# define __fallthrough __attribute__ ((fallthrough))
+# else
+# define __fallthrough
+# endif
+#endif
+
#endif /* _TOOLS_LINUX_COMPILER_H */
diff --git a/tools/lib/lockdep/uinclude/linux/lockdep.h b/tools/lib/lockdep/uinclude/linux/lockdep.h
index c808c7d02d21..e69118b2077e 100644
--- a/tools/lib/lockdep/uinclude/linux/lockdep.h
+++ b/tools/lib/lockdep/uinclude/linux/lockdep.h
@@ -8,7 +8,7 @@
#include <linux/utsname.h>
#include <linux/compiler.h>
-#define MAX_LOCK_DEPTH 2000UL
+#define MAX_LOCK_DEPTH 255UL
#define asmlinkage
#define __visible
diff --git a/tools/lib/traceevent/plugin_sched_switch.c b/tools/lib/traceevent/plugin_sched_switch.c
index f1ce60065258..ec30c2fcbac0 100644
--- a/tools/lib/traceevent/plugin_sched_switch.c
+++ b/tools/lib/traceevent/plugin_sched_switch.c
@@ -111,7 +111,7 @@ static int sched_switch_handler(struct trace_seq *s,
trace_seq_printf(s, "%lld ", val);
if (pevent_get_field_val(s, event, "prev_prio", record, &val, 0) == 0)
- trace_seq_printf(s, "[%lld] ", val);
+ trace_seq_printf(s, "[%d] ", (int) val);
if (pevent_get_field_val(s, event, "prev_state", record, &val, 0) == 0)
write_state(s, val);
@@ -129,7 +129,7 @@ static int sched_switch_handler(struct trace_seq *s,
trace_seq_printf(s, "%lld", val);
if (pevent_get_field_val(s, event, "next_prio", record, &val, 0) == 0)
- trace_seq_printf(s, " [%lld]", val);
+ trace_seq_printf(s, " [%d]", (int) val);
return 0;
}
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 74c265e0ffa0..fb1c9ddc3478 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -566,9 +566,9 @@ install-tests: all install-gtk
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \
$(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
-install-bin: install-tools install-tests
+install-bin: install-tools install-tests install-traceevent-plugins
-install: install-bin try-install-man install-traceevent-plugins
+install: install-bin try-install-man
install-python_ext:
$(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)'
diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c
index d28c1b6a3b54..fa5d17af88b7 100644
--- a/tools/perf/arch/x86/tests/intel-cqm.c
+++ b/tools/perf/arch/x86/tests/intel-cqm.c
@@ -17,7 +17,7 @@ static pid_t spawn(void)
if (pid)
return pid;
- while(1);
+ while(1)
sleep(5);
return 0;
}
diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c
index 9223c164e545..1f86ee8fb831 100644
--- a/tools/perf/arch/x86/util/dwarf-regs.c
+++ b/tools/perf/arch/x86/util/dwarf-regs.c
@@ -63,6 +63,8 @@ struct pt_regs_offset {
# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
#endif
+/* TODO: switching by dwarf address size */
+#ifndef __x86_64__
static const struct pt_regs_offset x86_32_regoffset_table[] = {
REG_OFFSET_NAME_32("%ax", eax),
REG_OFFSET_NAME_32("%cx", ecx),
@@ -75,6 +77,8 @@ static const struct pt_regs_offset x86_32_regoffset_table[] = {
REG_OFFSET_END,
};
+#define regoffset_table x86_32_regoffset_table
+#else
static const struct pt_regs_offset x86_64_regoffset_table[] = {
REG_OFFSET_NAME_64("%ax", rax),
REG_OFFSET_NAME_64("%dx", rdx),
@@ -95,11 +99,7 @@ static const struct pt_regs_offset x86_64_regoffset_table[] = {
REG_OFFSET_END,
};
-/* TODO: switching by dwarf address size */
-#ifdef __x86_64__
#define regoffset_table x86_64_regoffset_table
-#else
-#define regoffset_table x86_32_regoffset_table
#endif
/* Minus 1 for the ending REG_OFFSET_END */
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c
index 492df2752a2d..b4eb5b679081 100644
--- a/tools/perf/bench/numa.c
+++ b/tools/perf/bench/numa.c
@@ -1570,13 +1570,13 @@ static int __bench_numa(const char *name)
"GB/sec,", "total-speed", "GB/sec total speed");
if (g->p.show_details >= 2) {
- char tname[32];
+ char tname[14 + 2 * 10 + 1];
struct thread_data *td;
for (p = 0; p < g->p.nr_proc; p++) {
for (t = 0; t < g->p.nr_threads; t++) {
- memset(tname, 0, 32);
+ memset(tname, 0, sizeof(tname));
td = g->threads + p*g->p.nr_threads + t;
- snprintf(tname, 32, "process%d:thread%d", p, t);
+ snprintf(tname, sizeof(tname), "process%d:thread%d", p, t);
print_res(tname, td->speed_gbs,
"GB/sec", "thread-speed", "GB/sec/thread speed");
print_res(tname, td->system_time_ns / 1e9,
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 368d1e1561f7..48840556bf2d 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1253,21 +1253,19 @@ static int is_directory(const char *base_path, const struct dirent *dent)
return S_ISDIR(st.st_mode);
}
-#define for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next)\
- while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \
- lang_next) \
- if ((lang_dirent.d_type == DT_DIR || \
- (lang_dirent.d_type == DT_UNKNOWN && \
- is_directory(scripts_path, &lang_dirent))) && \
- (strcmp(lang_dirent.d_name, ".")) && \
- (strcmp(lang_dirent.d_name, "..")))
-
-#define for_each_script(lang_path, lang_dir, script_dirent, script_next)\
- while (!readdir_r(lang_dir, &script_dirent, &script_next) && \
- script_next) \
- if (script_dirent.d_type != DT_DIR && \
- (script_dirent.d_type != DT_UNKNOWN || \
- !is_directory(lang_path, &script_dirent)))
+#define for_each_lang(scripts_path, scripts_dir, lang_dirent) \
+ while ((lang_dirent = readdir(scripts_dir)) != NULL) \
+ if ((lang_dirent->d_type == DT_DIR || \
+ (lang_dirent->d_type == DT_UNKNOWN && \
+ is_directory(scripts_path, lang_dirent))) && \
+ (strcmp(lang_dirent->d_name, ".")) && \
+ (strcmp(lang_dirent->d_name, "..")))
+
+#define for_each_script(lang_path, lang_dir, script_dirent) \
+ while ((script_dirent = readdir(lang_dir)) != NULL) \
+ if (script_dirent->d_type != DT_DIR && \
+ (script_dirent->d_type != DT_UNKNOWN || \
+ !is_directory(lang_path, script_dirent)))
#define RECORD_SUFFIX "-record"
@@ -1413,7 +1411,7 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
const char *s __maybe_unused,
int unset __maybe_unused)
{
- struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ struct dirent *script_dirent, *lang_dirent;
char scripts_path[MAXPATHLEN];
DIR *scripts_dir, *lang_dir;
char script_path[MAXPATHLEN];
@@ -1428,19 +1426,19 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
if (!scripts_dir)
return -1;
- for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
+ for_each_lang(scripts_path, scripts_dir, lang_dirent) {
snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
- lang_dirent.d_name);
+ lang_dirent->d_name);
lang_dir = opendir(lang_path);
if (!lang_dir)
continue;
- for_each_script(lang_path, lang_dir, script_dirent, script_next) {
- script_root = get_script_root(&script_dirent, REPORT_SUFFIX);
+ for_each_script(lang_path, lang_dir, script_dirent) {
+ script_root = get_script_root(script_dirent, REPORT_SUFFIX);
if (script_root) {
desc = script_desc__findnew(script_root);
snprintf(script_path, MAXPATHLEN, "%s/%s",
- lang_path, script_dirent.d_name);
+ lang_path, script_dirent->d_name);
read_script_info(desc, script_path);
free(script_root);
}
@@ -1528,7 +1526,7 @@ static int check_ev_match(char *dir_name, char *scriptname,
*/
int find_scripts(char **scripts_array, char **scripts_path_array)
{
- struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ struct dirent *script_dirent, *lang_dirent;
char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN];
DIR *scripts_dir, *lang_dir;
struct perf_session *session;
@@ -1551,9 +1549,9 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
return -1;
}
- for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
+ for_each_lang(scripts_path, scripts_dir, lang_dirent) {
snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path,
- lang_dirent.d_name);
+ lang_dirent->d_name);
#ifdef NO_LIBPERL
if (strstr(lang_path, "perl"))
continue;
@@ -1567,16 +1565,16 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
if (!lang_dir)
continue;
- for_each_script(lang_path, lang_dir, script_dirent, script_next) {
+ for_each_script(lang_path, lang_dir, script_dirent) {
/* Skip those real time scripts: xxxtop.p[yl] */
- if (strstr(script_dirent.d_name, "top."))
+ if (strstr(script_dirent->d_name, "top."))
continue;
sprintf(scripts_path_array[i], "%s/%s", lang_path,
- script_dirent.d_name);
- temp = strchr(script_dirent.d_name, '.');
+ script_dirent->d_name);
+ temp = strchr(script_dirent->d_name, '.');
snprintf(scripts_array[i],
- (temp - script_dirent.d_name) + 1,
- "%s", script_dirent.d_name);
+ (temp - script_dirent->d_name) + 1,
+ "%s", script_dirent->d_name);
if (check_ev_match(lang_path,
scripts_array[i], session))
@@ -1594,7 +1592,7 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
static char *get_script_path(const char *script_root, const char *suffix)
{
- struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ struct dirent *script_dirent, *lang_dirent;
char scripts_path[MAXPATHLEN];
char script_path[MAXPATHLEN];
DIR *scripts_dir, *lang_dir;
@@ -1607,21 +1605,21 @@ static char *get_script_path(const char *script_root, const char *suffix)
if (!scripts_dir)
return NULL;
- for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
+ for_each_lang(scripts_path, scripts_dir, lang_dirent) {
snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
- lang_dirent.d_name);
+ lang_dirent->d_name);
lang_dir = opendir(lang_path);
if (!lang_dir)
continue;
- for_each_script(lang_path, lang_dir, script_dirent, script_next) {
- __script_root = get_script_root(&script_dirent, suffix);
+ for_each_script(lang_path, lang_dir, script_dirent) {
+ __script_root = get_script_root(script_dirent, suffix);
if (__script_root && !strcmp(script_root, __script_root)) {
free(__script_root);
closedir(lang_dir);
closedir(scripts_dir);
snprintf(script_path, MAXPATHLEN, "%s/%s",
- lang_path, script_dirent.d_name);
+ lang_path, script_dirent->d_name);
return strdup(script_path);
}
free(__script_root);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 7e2e72e6d9d1..4a8a02c302d2 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -636,7 +636,7 @@ repeat:
case -1:
if (errno == EINTR)
continue;
- /* Fall trhu */
+ __fallthrough;
default:
c = getc(stdin);
tcsetattr(0, TCSAFLUSH, &save);
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index c783d8fd3a80..ebe7115c751a 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1617,6 +1617,7 @@ static int trace__process_event(struct trace *trace, struct machine *machine,
color_fprintf(trace->output, PERF_COLOR_RED,
"LOST %" PRIu64 " events!\n", event->lost.lost);
ret = machine__process_lost_event(machine, event, sample);
+ break;
default:
ret = machine__process_event(machine, event, sample);
break;
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 636d7b42d844..54af2f2e2ee4 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -1727,15 +1727,14 @@ static int test_pmu_events(void)
}
while (!ret && (ent = readdir(dir))) {
-#define MAX_NAME 100
struct evlist_test e;
- char name[MAX_NAME];
+ char name[2 * NAME_MAX + 1 + 12 + 3];
if (!strcmp(ent->d_name, ".") ||
!strcmp(ent->d_name, ".."))
continue;
- snprintf(name, MAX_NAME, "cpu/event=%s/u", ent->d_name);
+ snprintf(name, sizeof(name), "cpu/event=%s/u", ent->d_name);
e.name = name;
e.check = test__checkevent_pmu_events;
@@ -1743,11 +1742,10 @@ static int test_pmu_events(void)
ret = test_event(&e);
if (ret)
break;
- snprintf(name, MAX_NAME, "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name);
+ snprintf(name, sizeof(name), "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name);
e.name = name;
e.check = test__checkevent_pmu_events_mix;
ret = test_event(&e);
-#undef MAX_NAME
}
closedir(dir);
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index e9703c0829f1..07b5f5951b25 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -702,7 +702,7 @@ static void __ui_browser__line_arrow_down(struct ui_browser *browser,
ui_browser__gotorc(browser, row, column + 1);
SLsmg_draw_hline(2);
- if (row++ == 0)
+ if (++row == 0)
goto out;
} else
row = 0;
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index d4d7cc27252f..718bd46d47fa 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -755,11 +755,11 @@ static int annotate_browser__run(struct annotate_browser *browser,
nd = browser->curr_hot;
break;
case K_UNTAB:
- if (nd != NULL)
+ if (nd != NULL) {
nd = rb_next(nd);
if (nd == NULL)
nd = rb_first(&browser->entries);
- else
+ } else
nd = browser->curr_hot;
break;
case K_F1:
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 956187bf1a85..26cba64345e3 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -416,7 +416,7 @@ static int __event__synthesize_thread(union perf_event *comm_event,
{
char filename[PATH_MAX];
DIR *tasks;
- struct dirent dirent, *next;
+ struct dirent *dirent;
pid_t tgid, ppid;
int rc = 0;
@@ -445,11 +445,11 @@ static int __event__synthesize_thread(union perf_event *comm_event,
return 0;
}
- while (!readdir_r(tasks, &dirent, &next) && next) {
+ while ((dirent = readdir(tasks)) != NULL) {
char *end;
pid_t _pid;
- _pid = strtol(dirent.d_name, &end, 10);
+ _pid = strtol(dirent->d_name, &end, 10);
if (*end)
continue;
@@ -558,7 +558,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
{
DIR *proc;
char proc_path[PATH_MAX];
- struct dirent dirent, *next;
+ struct dirent *dirent;
union perf_event *comm_event, *mmap_event, *fork_event;
int err = -1;
@@ -583,9 +583,9 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
if (proc == NULL)
goto out_free_fork;
- while (!readdir_r(proc, &dirent, &next) && next) {
+ while ((dirent = readdir(proc)) != NULL) {
char *end;
- pid_t pid = strtol(dirent.d_name, &end, 10);
+ pid_t pid = strtol(dirent->d_name, &end, 10);
if (*end) /* only interested in proper numerical dirents */
continue;
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
index 71df7acf8643..eeeae0629ad3 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <stdint.h>
#include <inttypes.h>
+#include <linux/compiler.h>
#include "../cache.h"
#include "../util.h"
@@ -63,6 +64,25 @@ enum intel_pt_pkt_state {
INTEL_PT_STATE_FUP_NO_TIP,
};
+static inline bool intel_pt_sample_time(enum intel_pt_pkt_state pkt_state)
+{
+ switch (pkt_state) {
+ case INTEL_PT_STATE_NO_PSB:
+ case INTEL_PT_STATE_NO_IP:
+ case INTEL_PT_STATE_ERR_RESYNC:
+ case INTEL_PT_STATE_IN_SYNC:
+ case INTEL_PT_STATE_TNT:
+ return true;
+ case INTEL_PT_STATE_TIP:
+ case INTEL_PT_STATE_TIP_PGD:
+ case INTEL_PT_STATE_FUP:
+ case INTEL_PT_STATE_FUP_NO_TIP:
+ return false;
+ default:
+ return true;
+ };
+}
+
#ifdef INTEL_PT_STRICT
#define INTEL_PT_STATE_ERR1 INTEL_PT_STATE_NO_PSB
#define INTEL_PT_STATE_ERR2 INTEL_PT_STATE_NO_PSB
@@ -90,6 +110,7 @@ struct intel_pt_decoder {
bool have_tma;
bool have_cyc;
bool fixup_last_mtc;
+ bool have_last_ip;
uint64_t pos;
uint64_t last_ip;
uint64_t ip;
@@ -97,6 +118,7 @@ struct intel_pt_decoder {
uint64_t timestamp;
uint64_t tsc_timestamp;
uint64_t ref_timestamp;
+ uint64_t sample_timestamp;
uint64_t ret_addr;
uint64_t ctc_timestamp;
uint64_t ctc_delta;
@@ -124,8 +146,6 @@ struct intel_pt_decoder {
bool have_calc_cyc_to_tsc;
int exec_mode;
unsigned int insn_bytes;
- uint64_t sign_bit;
- uint64_t sign_bits;
uint64_t period;
enum intel_pt_period_type period_type;
uint64_t tot_insn_cnt;
@@ -139,6 +159,7 @@ struct intel_pt_decoder {
unsigned int fup_tx_flags;
unsigned int tx_flags;
uint64_t timestamp_insn_cnt;
+ uint64_t sample_insn_cnt;
uint64_t stuck_ip;
int no_progress;
int stuck_ip_prd;
@@ -192,9 +213,6 @@ struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params)
decoder->data = params->data;
decoder->return_compression = params->return_compression;
- decoder->sign_bit = (uint64_t)1 << 47;
- decoder->sign_bits = ~(((uint64_t)1 << 48) - 1);
-
decoder->period = params->period;
decoder->period_type = params->period_type;
@@ -363,21 +381,30 @@ int intel_pt__strerror(int code, char *buf, size_t buflen)
return 0;
}
-static uint64_t intel_pt_calc_ip(struct intel_pt_decoder *decoder,
- const struct intel_pt_pkt *packet,
+static uint64_t intel_pt_calc_ip(const struct intel_pt_pkt *packet,
uint64_t last_ip)
{
uint64_t ip;
switch (packet->count) {
- case 2:
+ case 1:
ip = (last_ip & (uint64_t)0xffffffffffff0000ULL) |
packet->payload;
break;
- case 4:
+ case 2:
ip = (last_ip & (uint64_t)0xffffffff00000000ULL) |
packet->payload;
break;
+ case 3:
+ ip = packet->payload;
+ /* Sign-extend 6-byte ip */
+ if (ip & (uint64_t)0x800000000000ULL)
+ ip |= (uint64_t)0xffff000000000000ULL;
+ break;
+ case 4:
+ ip = (last_ip & (uint64_t)0xffff000000000000ULL) |
+ packet->payload;
+ break;
case 6:
ip = packet->payload;
break;
@@ -385,16 +412,13 @@ static uint64_t intel_pt_calc_ip(struct intel_pt_decoder *decoder,
return 0;
}
- if (ip & decoder->sign_bit)
- return ip | decoder->sign_bits;
-
return ip;
}
static inline void intel_pt_set_last_ip(struct intel_pt_decoder *decoder)
{
- decoder->last_ip = intel_pt_calc_ip(decoder, &decoder->packet,
- decoder->last_ip);
+ decoder->last_ip = intel_pt_calc_ip(&decoder->packet, decoder->last_ip);
+ decoder->have_last_ip = true;
}
static inline void intel_pt_set_ip(struct intel_pt_decoder *decoder)
@@ -895,6 +919,7 @@ static int intel_pt_walk_insn(struct intel_pt_decoder *decoder,
decoder->tot_insn_cnt += insn_cnt;
decoder->timestamp_insn_cnt += insn_cnt;
+ decoder->sample_insn_cnt += insn_cnt;
decoder->period_insn_cnt += insn_cnt;
if (err) {
@@ -1413,7 +1438,8 @@ static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder)
case INTEL_PT_FUP:
decoder->pge = true;
- intel_pt_set_last_ip(decoder);
+ if (decoder->packet.count)
+ intel_pt_set_last_ip(decoder);
break;
case INTEL_PT_MODE_TSX:
@@ -1617,6 +1643,8 @@ next:
break;
case INTEL_PT_PSB:
+ decoder->last_ip = 0;
+ decoder->have_last_ip = true;
intel_pt_clear_stack(&decoder->stack);
err = intel_pt_walk_psbend(decoder);
if (err == -EAGAIN)
@@ -1695,6 +1723,13 @@ next:
}
}
+static inline bool intel_pt_have_ip(struct intel_pt_decoder *decoder)
+{
+ return decoder->packet.count &&
+ (decoder->have_last_ip || decoder->packet.count == 3 ||
+ decoder->packet.count == 6);
+}
+
/* Walk PSB+ packets to get in sync. */
static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
{
@@ -1708,6 +1743,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
decoder->continuous_period = false;
+ __fallthrough;
case INTEL_PT_TIP_PGE:
case INTEL_PT_TIP:
intel_pt_log("ERROR: Unexpected packet\n");
@@ -1715,8 +1751,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
case INTEL_PT_FUP:
decoder->pge = true;
- if (decoder->last_ip || decoder->packet.count == 6 ||
- decoder->packet.count == 0) {
+ if (intel_pt_have_ip(decoder)) {
uint64_t current_ip = decoder->ip;
intel_pt_set_ip(decoder);
@@ -1762,6 +1797,8 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
decoder->pge = false;
decoder->continuous_period = false;
intel_pt_clear_tx_flags(decoder);
+ __fallthrough;
+
case INTEL_PT_TNT:
decoder->have_tma = false;
intel_pt_log("ERROR: Unexpected packet\n");
@@ -1802,27 +1839,21 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder)
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
decoder->continuous_period = false;
+ __fallthrough;
case INTEL_PT_TIP_PGE:
case INTEL_PT_TIP:
decoder->pge = decoder->packet.type != INTEL_PT_TIP_PGD;
- if (decoder->last_ip || decoder->packet.count == 6 ||
- decoder->packet.count == 0)
+ if (intel_pt_have_ip(decoder))
intel_pt_set_ip(decoder);
if (decoder->ip)
return 0;
break;
case INTEL_PT_FUP:
- if (decoder->overflow) {
- if (decoder->last_ip ||
- decoder->packet.count == 6 ||
- decoder->packet.count == 0)
- intel_pt_set_ip(decoder);
- if (decoder->ip)
- return 0;
- }
- if (decoder->packet.count)
- intel_pt_set_last_ip(decoder);
+ if (intel_pt_have_ip(decoder))
+ intel_pt_set_ip(decoder);
+ if (decoder->ip)
+ return 0;
break;
case INTEL_PT_MTC:
@@ -1871,6 +1902,9 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder)
break;
case INTEL_PT_PSB:
+ decoder->last_ip = 0;
+ decoder->have_last_ip = true;
+ intel_pt_clear_stack(&decoder->stack);
err = intel_pt_walk_psb(decoder);
if (err)
return err;
@@ -1896,6 +1930,8 @@ static int intel_pt_sync_ip(struct intel_pt_decoder *decoder)
{
int err;
+ decoder->set_fup_tx_flags = false;
+
intel_pt_log("Scanning for full IP\n");
err = intel_pt_walk_to_ip(decoder);
if (err)
@@ -2004,6 +2040,7 @@ static int intel_pt_sync(struct intel_pt_decoder *decoder)
decoder->pge = false;
decoder->continuous_period = false;
+ decoder->have_last_ip = false;
decoder->last_ip = 0;
decoder->ip = 0;
intel_pt_clear_stack(&decoder->stack);
@@ -2012,6 +2049,7 @@ static int intel_pt_sync(struct intel_pt_decoder *decoder)
if (err)
return err;
+ decoder->have_last_ip = true;
decoder->pkt_state = INTEL_PT_STATE_NO_IP;
err = intel_pt_walk_psb(decoder);
@@ -2030,7 +2068,7 @@ static int intel_pt_sync(struct intel_pt_decoder *decoder)
static uint64_t intel_pt_est_timestamp(struct intel_pt_decoder *decoder)
{
- uint64_t est = decoder->timestamp_insn_cnt << 1;
+ uint64_t est = decoder->sample_insn_cnt << 1;
if (!decoder->cbr || !decoder->max_non_turbo_ratio)
goto out;
@@ -2038,7 +2076,7 @@ static uint64_t intel_pt_est_timestamp(struct intel_pt_decoder *decoder)
est *= decoder->max_non_turbo_ratio;
est /= decoder->cbr;
out:
- return decoder->timestamp + est;
+ return decoder->sample_timestamp + est;
}
const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder)
@@ -2054,7 +2092,9 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder)
err = intel_pt_sync(decoder);
break;
case INTEL_PT_STATE_NO_IP:
+ decoder->have_last_ip = false;
decoder->last_ip = 0;
+ decoder->ip = 0;
/* Fall through */
case INTEL_PT_STATE_ERR_RESYNC:
err = intel_pt_sync_ip(decoder);
@@ -2091,15 +2131,24 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder)
}
} while (err == -ENOLINK);
- decoder->state.err = err ? intel_pt_ext_err(err) : 0;
- decoder->state.timestamp = decoder->timestamp;
+ if (err) {
+ decoder->state.err = intel_pt_ext_err(err);
+ decoder->state.from_ip = decoder->ip;
+ decoder->sample_timestamp = decoder->timestamp;
+ decoder->sample_insn_cnt = decoder->timestamp_insn_cnt;
+ } else {
+ decoder->state.err = 0;
+ if (intel_pt_sample_time(decoder->pkt_state)) {
+ decoder->sample_timestamp = decoder->timestamp;
+ decoder->sample_insn_cnt = decoder->timestamp_insn_cnt;
+ }
+ }
+
+ decoder->state.timestamp = decoder->sample_timestamp;
decoder->state.est_timestamp = intel_pt_est_timestamp(decoder);
decoder->state.cr3 = decoder->cr3;
decoder->state.tot_insn_cnt = decoder->tot_insn_cnt;
- if (err)
- decoder->state.from_ip = decoder->ip;
-
return &decoder->state;
}
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
index b1257c816310..7528ae4f7e28 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
@@ -17,6 +17,7 @@
#include <string.h>
#include <endian.h>
#include <byteswap.h>
+#include <linux/compiler.h>
#include "intel-pt-pkt-decoder.h"
@@ -292,36 +293,46 @@ static int intel_pt_get_ip(enum intel_pt_pkt_type type, unsigned int byte,
const unsigned char *buf, size_t len,
struct intel_pt_pkt *packet)
{
- switch (byte >> 5) {
+ int ip_len;
+
+ packet->count = byte >> 5;
+
+ switch (packet->count) {
case 0:
- packet->count = 0;
+ ip_len = 0;
break;
case 1:
if (len < 3)
return INTEL_PT_NEED_MORE_BYTES;
- packet->count = 2;
+ ip_len = 2;
packet->payload = le16_to_cpu(*(uint16_t *)(buf + 1));
break;
case 2:
if (len < 5)
return INTEL_PT_NEED_MORE_BYTES;
- packet->count = 4;
+ ip_len = 4;
packet->payload = le32_to_cpu(*(uint32_t *)(buf + 1));
break;
case 3:
- case 6:
+ case 4:
if (len < 7)
return INTEL_PT_NEED_MORE_BYTES;
- packet->count = 6;
+ ip_len = 6;
memcpy_le64(&packet->payload, buf + 1, 6);
break;
+ case 6:
+ if (len < 9)
+ return INTEL_PT_NEED_MORE_BYTES;
+ ip_len = 8;
+ packet->payload = le64_to_cpu(*(uint64_t *)(buf + 1));
+ break;
default:
return INTEL_PT_BAD_PACKET;
}
packet->type = type;
- return packet->count + 1;
+ return ip_len + 1;
}
static int intel_pt_get_mode(const unsigned char *buf, size_t len,
@@ -488,6 +499,7 @@ int intel_pt_pkt_desc(const struct intel_pt_pkt *packet, char *buf,
case INTEL_PT_FUP:
if (!(packet->count))
return snprintf(buf, buf_len, "%s no ip", name);
+ __fallthrough;
case INTEL_PT_CYC:
case INTEL_PT_VMCS:
case INTEL_PT_MTC:
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 854dd2105bd5..881bbb5e7912 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -138,11 +138,11 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT)
-#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
- while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
- if (sys_dirent.d_type == DT_DIR && \
- (strcmp(sys_dirent.d_name, ".")) && \
- (strcmp(sys_dirent.d_name, "..")))
+#define for_each_subsystem(sys_dir, sys_dirent) \
+ while ((sys_dirent = readdir(sys_dir)) != NULL) \
+ if (sys_dirent->d_type == DT_DIR && \
+ (strcmp(sys_dirent->d_name, ".")) && \
+ (strcmp(sys_dirent->d_name, "..")))
static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
{
@@ -159,12 +159,12 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
return 0;
}
-#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \
- while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
- if (evt_dirent.d_type == DT_DIR && \
- (strcmp(evt_dirent.d_name, ".")) && \
- (strcmp(evt_dirent.d_name, "..")) && \
- (!tp_event_has_id(&sys_dirent, &evt_dirent)))
+#define for_each_event(sys_dirent, evt_dir, evt_dirent) \
+ while ((evt_dirent = readdir(evt_dir)) != NULL) \
+ if (evt_dirent->d_type == DT_DIR && \
+ (strcmp(evt_dirent->d_name, ".")) && \
+ (strcmp(evt_dirent->d_name, "..")) && \
+ (!tp_event_has_id(sys_dirent, evt_dirent)))
#define MAX_EVENT_LENGTH 512
@@ -173,7 +173,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
{
struct tracepoint_path *path = NULL;
DIR *sys_dir, *evt_dir;
- struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ struct dirent *sys_dirent, *evt_dirent;
char id_buf[24];
int fd;
u64 id;
@@ -184,18 +184,18 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
if (!sys_dir)
return NULL;
- for_each_subsystem(sys_dir, sys_dirent, sys_next) {
+ for_each_subsystem(sys_dir, sys_dirent) {
snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,
- sys_dirent.d_name);
+ sys_dirent->d_name);
evt_dir = opendir(dir_path);
if (!evt_dir)
continue;
- for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
+ for_each_event(sys_dirent, evt_dir, evt_dirent) {
snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path,
- evt_dirent.d_name);
+ evt_dirent->d_name);
fd = open(evt_path, O_RDONLY);
if (fd < 0)
continue;
@@ -220,9 +220,9 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
free(path);
return NULL;
}
- strncpy(path->system, sys_dirent.d_name,
+ strncpy(path->system, sys_dirent->d_name,
MAX_EVENT_LENGTH);
- strncpy(path->name, evt_dirent.d_name,
+ strncpy(path->name, evt_dirent->d_name,
MAX_EVENT_LENGTH);
return path;
}
@@ -1662,7 +1662,7 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
bool name_only)
{
DIR *sys_dir, *evt_dir;
- struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ struct dirent *sys_dirent, *evt_dirent;
char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN];
char **evt_list = NULL;
@@ -1680,20 +1680,20 @@ restart:
goto out_close_sys_dir;
}
- for_each_subsystem(sys_dir, sys_dirent, sys_next) {
+ for_each_subsystem(sys_dir, sys_dirent) {
if (subsys_glob != NULL &&
- !strglobmatch(sys_dirent.d_name, subsys_glob))
+ !strglobmatch(sys_dirent->d_name, subsys_glob))
continue;
snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,
- sys_dirent.d_name);
+ sys_dirent->d_name);
evt_dir = opendir(dir_path);
if (!evt_dir)
continue;
- for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
+ for_each_event(sys_dirent, evt_dir, evt_dirent) {
if (event_glob != NULL &&
- !strglobmatch(evt_dirent.d_name, event_glob))
+ !strglobmatch(evt_dirent->d_name, event_glob))
continue;
if (!evt_num_known) {
@@ -1702,7 +1702,7 @@ restart:
}
snprintf(evt_path, MAXPATHLEN, "%s:%s",
- sys_dirent.d_name, evt_dirent.d_name);
+ sys_dirent->d_name, evt_dirent->d_name);
evt_list[evt_i] = strdup(evt_path);
if (evt_list[evt_i] == NULL)
@@ -1755,7 +1755,7 @@ out_close_sys_dir:
int is_valid_tracepoint(const char *event_string)
{
DIR *sys_dir, *evt_dir;
- struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ struct dirent *sys_dirent, *evt_dirent;
char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN];
@@ -1763,17 +1763,17 @@ int is_valid_tracepoint(const char *event_string)
if (!sys_dir)
return 0;
- for_each_subsystem(sys_dir, sys_dirent, sys_next) {
+ for_each_subsystem(sys_dir, sys_dirent) {
snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,
- sys_dirent.d_name);
+ sys_dirent->d_name);
evt_dir = opendir(dir_path);
if (!evt_dir)
continue;
- for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
+ for_each_event(sys_dirent, evt_dir, evt_dirent) {
snprintf(evt_path, MAXPATHLEN, "%s:%s",
- sys_dirent.d_name, evt_dirent.d_name);
+ sys_dirent->d_name, evt_dirent->d_name);
if (!strcmp(evt_path, event_string)) {
closedir(evt_dir);
closedir(sys_dir);
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 6f2a0279476c..593066c68e3d 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -153,7 +153,7 @@ static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *n
if (fd == -1)
return -1;
- sret = read(fd, alias->unit, UNIT_MAX_LEN);
+ sret = read(fd, alias->unit, UNIT_MAX_LEN);
if (sret < 0)
goto error;
diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scripting-engines/Build
index 6516e220c247..82d28c67e0f3 100644
--- a/tools/perf/util/scripting-engines/Build
+++ b/tools/perf/util/scripting-engines/Build
@@ -1,6 +1,6 @@
libperf-$(CONFIG_LIBPERL) += trace-event-perl.o
libperf-$(CONFIG_LIBPYTHON) += trace-event-python.o
-CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-undef -Wno-switch-default
+CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-nested-externs -Wno-undef -Wno-switch-default
CFLAGS_trace-event-python.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c
index bcae659b6546..efb53772e0ec 100644
--- a/tools/perf/util/strfilter.c
+++ b/tools/perf/util/strfilter.c
@@ -269,6 +269,7 @@ static int strfilter_node__sprint(struct strfilter_node *node, char *buf)
len = strfilter_node__sprint_pt(node->l, buf);
if (len < 0)
return len;
+ __fallthrough;
case '!':
if (buf) {
*(buf + len++) = *node->p;
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index fc8781de62db..accb7ece1d3c 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -21,6 +21,8 @@ s64 perf_atoll(const char *str)
case 'b': case 'B':
if (*p)
goto out_err;
+
+ __fallthrough;
case '\0':
return length;
default:
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 27ae382feb2d..7c97ecaeae48 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -488,6 +488,12 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
break;
} else {
int n = namesz + descsz;
+
+ if (n > (int)sizeof(bf)) {
+ n = sizeof(bf);
+ pr_debug("%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n",
+ __func__, filename, nhdr.n_namesz, nhdr.n_descsz);
+ }
if (read(fd, bf, n) != n)
break;
}
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 0a9ae8014729..829508a21448 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -227,7 +227,7 @@ void thread__find_cpumode_addr_location(struct thread *thread,
struct addr_location *al)
{
size_t i;
- const u8 const cpumodes[] = {
+ const u8 cpumodes[] = {
PERF_RECORD_MISC_USER,
PERF_RECORD_MISC_KERNEL,
PERF_RECORD_MISC_GUEST_USER,
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index 6ec3c5ca438f..4e666b95b87e 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -92,8 +92,8 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
{
DIR *proc;
int max_threads = 32, items, i;
- char path[256];
- struct dirent dirent, *next, **namelist = NULL;
+ char path[NAME_MAX + 1 + 6];
+ struct dirent *dirent, **namelist = NULL;
struct thread_map *threads = thread_map__alloc(max_threads);
if (threads == NULL)
@@ -106,16 +106,16 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
threads->nr = 0;
atomic_set(&threads->refcnt, 1);
- while (!readdir_r(proc, &dirent, &next) && next) {
+ while ((dirent = readdir(proc)) != NULL) {
char *end;
bool grow = false;
struct stat st;
- pid_t pid = strtol(dirent.d_name, &end, 10);
+ pid_t pid = strtol(dirent->d_name, &end, 10);
if (*end) /* only interested in proper numerical dirents */
continue;
- snprintf(path, sizeof(path), "/proc/%s", dirent.d_name);
+ snprintf(path, sizeof(path), "/proc/%s", dirent->d_name);
if (stat(path, &st) != 0)
continue;
diff --git a/tools/testing/selftests/capabilities/test_execve.c b/tools/testing/selftests/capabilities/test_execve.c
index 10a21a958aaf..763f37fecfb8 100644
--- a/tools/testing/selftests/capabilities/test_execve.c
+++ b/tools/testing/selftests/capabilities/test_execve.c
@@ -138,9 +138,6 @@ static void chdir_to_tmpfs(void)
if (chdir(cwd) != 0)
err(1, "chdir to private tmpfs");
-
- if (umount2(".", MNT_DETACH) != 0)
- err(1, "detach private tmpfs");
}
static void copy_fromat_to(int fromfd, const char *fromname, const char *toname)
@@ -248,7 +245,7 @@ static int do_tests(int uid, const char *our_path)
err(1, "chown");
if (chmod("validate_cap_sgidnonroot", S_ISGID | 0710) != 0)
err(1, "chmod");
-}
+ }
capng_get_caps_process();
@@ -384,7 +381,7 @@ static int do_tests(int uid, const char *our_path)
} else {
printf("[RUN]\tNon-root +ia, sgidnonroot => i\n");
exec_other_validate_cap("./validate_cap_sgidnonroot",
- false, false, true, false);
+ false, false, true, false);
if (fork_wait()) {
printf("[RUN]\tNon-root +ia, sgidroot => i\n");
diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c
index 1dd087da6f31..111e09c3f4bf 100644
--- a/virt/kvm/vfio.c
+++ b/virt/kvm/vfio.c
@@ -47,6 +47,22 @@ static struct vfio_group *kvm_vfio_group_get_external_user(struct file *filep)
return vfio_group;
}
+static bool kvm_vfio_external_group_match_file(struct vfio_group *group,
+ struct file *filep)
+{
+ bool ret, (*fn)(struct vfio_group *, struct file *);
+
+ fn = symbol_get(vfio_external_group_match_file);
+ if (!fn)
+ return false;
+
+ ret = fn(group, filep);
+
+ symbol_put(vfio_external_group_match_file);
+
+ return ret;
+}
+
static void kvm_vfio_group_put_external_user(struct vfio_group *vfio_group)
{
void (*fn)(struct vfio_group *);
@@ -171,18 +187,13 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
if (!f.file)
return -EBADF;
- vfio_group = kvm_vfio_group_get_external_user(f.file);
- fdput(f);
-
- if (IS_ERR(vfio_group))
- return PTR_ERR(vfio_group);
-
ret = -ENOENT;
mutex_lock(&kv->lock);
list_for_each_entry(kvg, &kv->group_list, node) {
- if (kvg->vfio_group != vfio_group)
+ if (!kvm_vfio_external_group_match_file(kvg->vfio_group,
+ f.file))
continue;
list_del(&kvg->node);
@@ -196,7 +207,7 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
mutex_unlock(&kv->lock);
- kvm_vfio_group_put_external_user(vfio_group);
+ fdput(f);
kvm_vfio_update_coherency(dev);